def add_seq_with_content_groups(self, groups=None): """ Adds sequential and two content groups to first course in courses list. """ config_course_cohorts(self.courses[0], is_cohorted=True) if groups is None: groups = self.groups self.user_partition = UserPartition(id=0, name='Partition 1', description='This is partition 1', groups=groups, scheme=CohortPartitionScheme) self.user_partition.scheme.name = "cohort" ItemFactory.create( parent_location=self.chapter.location, category='sequential', display_name="Lesson 1", publish_item=True, metadata={u"user_partitions": [self.user_partition.to_json()]}) self.first_cohort, self.second_cohort = [ CohortFactory(course_id=self.courses[0].id) for _ in range(2) ] self.courses[0].user_partitions = [self.user_partition] self.courses[0].save() modulestore().update_item(self.courses[0], self.user.id)
def add_seq_with_content_groups(self, groups=None): """ Adds sequential and two content groups to first course in courses list. """ config_course_cohorts(self.courses[0], is_cohorted=True) if groups is None: groups = self.groups self.user_partition = UserPartition( id=0, name='Partition 1', description='This is partition 1', groups=groups, scheme=CohortPartitionScheme ) self.user_partition.scheme.name = "cohort" ItemFactory.create( parent_location=self.chapter.location, category='sequential', display_name="Lesson 1", publish_item=True, metadata={u"user_partitions": [self.user_partition.to_json()]} ) self.first_cohort, self.second_cohort = [ CohortFactory(course_id=self.courses[0].id) for _ in range(2) ] self.courses[0].user_partitions = [self.user_partition] self.courses[0].save() modulestore().update_item(self.courses[0], self.user.id)
def test_add_to_valid_cohort(self): config_course_cohorts(self.course, is_cohorted=True, manual_cohorts=["cohort1", "cohort2"]) response = self.request_bulk_enroll({ 'identifiers': self.notenrolled_student.username, 'action': 'enroll', 'email_students': False, 'courses': self.course_key, 'cohorts': "cohort1" }) self.assertEqual(response.status_code, 200) # test the response data expected = { "action": "enroll", 'auto_enroll': False, "email_students": False, "courses": { self.course_key: { "action": "enroll", 'auto_enroll': False, "results": [{ "identifier": self.notenrolled_student.username, "before": { "enrollment": False, "auto_enroll": False, "user": True, "allowed": False, "cohort": None, }, "after": { "enrollment": True, "auto_enroll": False, "user": True, "allowed": False, "cohort": 'cohort1', } }] } } } manual_enrollments = ManualEnrollmentAudit.objects.all() self.assertEqual(manual_enrollments.count(), 1) self.assertEqual(manual_enrollments[0].state_transition, UNENROLLED_TO_ENROLLED) res_json = json.loads(response.content.decode('utf-8')) self.assertIsNotNone( get_cohort_id(self.notenrolled_student, CourseKey.from_string(self.course_key))) self.assertEqual(res_json, expected)
def test_is_commentable_cohorted_team(self): course = modulestore().get_course(self.toy_course_key) self.assertFalse(cohorts.is_course_cohorted(course.id)) config_course_cohorts(course, is_cohorted=True) team = CourseTeamFactory(course_id=course.id) # Verify that team discussions are not cohorted, but other discussions are self.assertFalse(utils.is_commentable_cohorted(course.id, team.discussion_topic_id)) self.assertTrue(utils.is_commentable_cohorted(course.id, "random"))
def test_post_cohortmembership_fix(self): """ Test that changes made *after* migration, but *before* turning on new code are handled properly """ # First, we're going to simulate some problem states that can arise during this window config_course_cohorts(self.course1, is_cohorted=True, auto_cohorts=["Course1AutoGroup1", "Course1AutoGroup2"]) # Get the cohorts from the courses, which will cause auto cohorts to be created cohort_handler(self.request, unicode(self.course1.id)) course_1_auto_cohort_1 = get_cohort_by_name(self.course1.id, "Course1AutoGroup1") course_1_auto_cohort_2 = get_cohort_by_name(self.course1.id, "Course1AutoGroup2") # When migrations were first run, the users were assigned to CohortMemberships correctly membership1 = CohortMembership( course_id=course_1_auto_cohort_1.course_id, user=self.user1, course_user_group=course_1_auto_cohort_1 ) membership1.save() membership2 = CohortMembership( course_id=course_1_auto_cohort_1.course_id, user=self.user2, course_user_group=course_1_auto_cohort_1 ) membership2.save() # But before CohortMembership code was turned on, some changes were made: course_1_auto_cohort_2.users.add(self.user1) # user1 is now in 2 cohorts in the same course! course_1_auto_cohort_2.users.add(self.user2) course_1_auto_cohort_1.users.remove(self.user2) # and user2 was moved, but no one told CohortMembership! # run the post-CohortMembership command, dry-run call_command('post_cohort_membership_fix') # Verify nothing was changed in dry-run mode. self.assertEqual(self.user1.course_groups.count(), 2) # CourseUserGroup has 2 entries for user1 self.assertEqual(CohortMembership.objects.get(user=self.user2).course_user_group.name, 'Course1AutoGroup1') user2_cohorts = list(self.user2.course_groups.values_list('name', flat=True)) self.assertEqual(user2_cohorts, ['Course1AutoGroup2']) # CourseUserGroup and CohortMembership disagree # run the post-CohortMembership command, and commit it call_command('post_cohort_membership_fix', commit='commit') # verify that both databases agree about the (corrected) state of the memberships self.assertEqual(self.user1.course_groups.count(), 1) self.assertEqual(CohortMembership.objects.filter(user=self.user1).count(), 1) self.assertEqual(self.user2.course_groups.count(), 1) self.assertEqual(CohortMembership.objects.filter(user=self.user2).count(), 1) self.assertEqual(CohortMembership.objects.get(user=self.user2).course_user_group.name, 'Course1AutoGroup2') user2_cohorts = list(self.user2.course_groups.values_list('name', flat=True)) self.assertEqual(user2_cohorts, ['Course1AutoGroup2'])
def test_post_cohortmembership_fix(self): """ Test that changes made *after* migration, but *before* turning on new code are handled properly """ # First, we're going to simulate some problem states that can arise during this window config_course_cohorts(self.course1, is_cohorted=True, auto_cohorts=["Course1AutoGroup1", "Course1AutoGroup2"]) # Get the cohorts from the courses, which will cause auto cohorts to be created cohort_handler(self.request, unicode(self.course1.id)) course_1_auto_cohort_1 = get_cohort_by_name(self.course1.id, "Course1AutoGroup1") course_1_auto_cohort_2 = get_cohort_by_name(self.course1.id, "Course1AutoGroup2") # When migrations were first run, the users were assigned to CohortMemberships correctly membership1 = CohortMembership( course_id=course_1_auto_cohort_1.course_id, user=self.user1, course_user_group=course_1_auto_cohort_1 ) membership1.save() membership2 = CohortMembership( course_id=course_1_auto_cohort_1.course_id, user=self.user2, course_user_group=course_1_auto_cohort_1 ) membership2.save() # But before CohortMembership code was turned on, some changes were made: course_1_auto_cohort_2.users.add(self.user1) # user1 is now in 2 cohorts in the same course! course_1_auto_cohort_2.users.add(self.user2) course_1_auto_cohort_1.users.remove(self.user2) # and user2 was moved, but no one told CohortMembership! # run the post-CohortMembership command, dry-run call_command('post_cohort_membership_fix') # Verify nothing was changed in dry-run mode. self.assertEqual(self.user1.course_groups.count(), 2) # CourseUserGroup has 2 entries for user1 self.assertEqual(CohortMembership.objects.get(user=self.user2).course_user_group.name, 'Course1AutoGroup1') user2_cohorts = list(self.user2.course_groups.values_list('name', flat=True)) self.assertEqual(user2_cohorts, ['Course1AutoGroup2']) # CourseUserGroup and CohortMembership disagree # run the post-CohortMembership command, and commit it call_command('post_cohort_membership_fix', commit='commit') # verify that both databases agree about the (corrected) state of the memberships self.assertEqual(self.user1.course_groups.count(), 1) self.assertEqual(CohortMembership.objects.filter(user=self.user1).count(), 1) self.assertEqual(self.user2.course_groups.count(), 1) self.assertEqual(CohortMembership.objects.filter(user=self.user2).count(), 1) self.assertEqual(CohortMembership.objects.get(user=self.user2).course_user_group.name, 'Course1AutoGroup2') user2_cohorts = list(self.user2.course_groups.values_list('name', flat=True)) self.assertEqual(user2_cohorts, ['Course1AutoGroup2'])
def test_allow_cohorts_when_enrolling(self): """ Test if the cohorts are given but the action is unenroll. """ config_course_cohorts(self.course, is_cohorted=True, manual_cohorts=["cohort1", "cohort2"]) response = self.request_bulk_enroll({ 'identifiers': self.notenrolled_student.username, 'action': 'unenroll', 'email_students': False, 'cohorts': 'cohort1', 'courses': self.course_key }) self.assertContains(response, 'Cohorts can only be used for enrollments.', status_code=400)
def test_users_with_multiple_cohorts_cleanup(self): """ Test that user which have been added in multiple cohorts of a course, can get cohorts without error after running cohorts cleanup command """ # set two auto_cohort_groups for both courses config_course_cohorts( self.course1, [], cohorted=True, auto_cohort_groups=["Course1AutoGroup1", "Course1AutoGroup2"] ) config_course_cohorts( self.course2, [], cohorted=True, auto_cohort_groups=["Course2AutoGroup1", "Course2AutoGroup2"] ) # get the cohorts from the courses, which will cause auto cohorts to be created cohort_handler(self.request, unicode(self.course1.id)) cohort_handler(self.request, unicode(self.course2.id)) course_1_auto_cohort_1 = get_cohort_by_name(self.course1.id, "Course1AutoGroup1") course_1_auto_cohort_2 = get_cohort_by_name(self.course1.id, "Course1AutoGroup2") course_2_auto_cohort_1 = get_cohort_by_name(self.course2.id, "Course2AutoGroup1") # forcefully add user1 in two auto cohorts course_1_auto_cohort_1.users.add(self.user1) course_1_auto_cohort_2.users.add(self.user1) # forcefully add user2 in auto cohorts of both courses course_1_auto_cohort_1.users.add(self.user2) course_2_auto_cohort_1.users.add(self.user2) # now check that when user1 goes on discussion page and tries to get # cohorts 'MultipleObjectsReturned' exception is returned with self.assertRaises(MultipleObjectsReturned): get_cohort(self.user1, self.course1.id) # also check that user 2 can go on discussion page of both courses # without any exception get_cohort(self.user2, self.course1.id) get_cohort(self.user2, self.course2.id) # call command to remove users added in multiple cohorts of a course # are removed from all cohort groups call_command('remove_users_from_multiple_cohorts') # check that only user1 (with multiple cohorts) is removed from cohorts # and user2 is still in auto cohorts of both course after running # 'remove_users_from_multiple_cohorts' management command self.assertEqual(self.user1.course_groups.count(), 0) self.assertEqual(self.user2.course_groups.count(), 2) user2_cohorts = list(self.user2.course_groups.values_list('name', flat=True)) self.assertEqual(user2_cohorts, ['Course1AutoGroup1', 'Course2AutoGroup1']) # now check that user1 can get cohorts in which he is added response = cohort_handler(self.request, unicode(self.course1.id)) self.assertEqual(response.status_code, 200)
def test_add_to_valid_cohort(self): config_course_cohorts(self.course, is_cohorted=True, manual_cohorts=["cohort1", "cohort2"]) response = self.request_bulk_enroll({ 'identifiers': self.notenrolled_student.username, 'action': 'enroll', 'email_students': False, 'courses': self.course_key, 'cohorts': "cohort1" }) self.assertEqual(response.status_code, 200) # test the response data expected = { "action": "enroll", 'auto_enroll': False, "email_students": False, "courses": { self.course_key: { "action": "enroll", 'auto_enroll': False, "results": [ { "identifier": self.notenrolled_student.username, "before": { "enrollment": False, "auto_enroll": False, "user": True, "allowed": False, "cohort": None, }, "after": { "enrollment": True, "auto_enroll": False, "user": True, "allowed": False, "cohort": 'cohort1', } } ] } } } manual_enrollments = ManualEnrollmentAudit.objects.all() self.assertEqual(manual_enrollments.count(), 1) self.assertEqual(manual_enrollments[0].state_transition, UNENROLLED_TO_ENROLLED) res_json = json.loads(response.content) self.assertIsNotNone(get_cohort_id(self.notenrolled_student, CourseKey.from_string(self.course_key))) self.assertEqual(res_json, expected)
def test_allow_cohorts_when_enrolling(self): """ Test if the cohorts are given but the action is unenroll. """ config_course_cohorts(self.course, is_cohorted=True, manual_cohorts=["cohort1", "cohort2"]) response = self.request_bulk_enroll({ 'identifiers': self.notenrolled_student.username, 'action': 'unenroll', 'email_students': False, 'cohorts': 'cohort1', 'courses': self.course_key }) self.assertEqual(response.status_code, 400) self.assertIn('Cohorts can only be used for enrollments.', response.content)
def setup_chorts(self, course): """ Sets up a cohort for each previously created user partition. """ for user_partition in self.user_partitions: config_course_cohorts(course, is_cohorted=True) self.cohorts = [] for group in self.groups: cohort = CohortFactory(course_id=course.id) self.cohorts.append(cohort) link_cohort_to_partition_group( cohort, user_partition.id, group.id, )
def setup_cohorts(self, course): """ Sets up a cohort for each previously created user partition. """ config_course_cohorts(course, is_cohorted=True) self.partition_cohorts = [] for user_partition in self.user_partitions: partition_cohorts = [] for group in self.groups: cohort = CohortFactory(course_id=course.id) partition_cohorts.append(cohort) link_cohort_to_partition_group( cohort, user_partition.id, group.id, ) self.partition_cohorts.append(partition_cohorts)
def test_is_commentable_cohorted_inline_discussion(self): course = modulestore().get_course(self.toy_course_key) self.assertFalse(cohorts.is_course_cohorted(course.id)) def to_id(name): # pylint: disable=missing-docstring return topic_name_to_id(course, name) config_course_cohorts( course, is_cohorted=True, discussion_topics=["General", "Feedback"], cohorted_discussions=["Feedback", "random_inline"], ) self.assertTrue( utils.is_commentable_cohorted(course.id, to_id("random")), "By default, Non-top-level discussion is always cohorted in cohorted courses.", ) # if always_cohort_inline_discussions is set to False, non-top-level discussion are always # non cohorted unless they are explicitly set in cohorted_discussions config_course_cohorts( course, is_cohorted=True, discussion_topics=["General", "Feedback"], cohorted_discussions=["Feedback", "random_inline"], always_cohort_inline_discussions=False, ) self.assertFalse( utils.is_commentable_cohorted(course.id, to_id("random")), "Non-top-level discussion is not cohorted if always_cohort_inline_discussions is False.", ) self.assertTrue( utils.is_commentable_cohorted(course.id, to_id("random_inline")), "If always_cohort_inline_discussions set to False, Non-top-level discussion is " "cohorted if explicitly set in cohorted_discussions.", ) self.assertTrue( utils.is_commentable_cohorted(course.id, to_id("Feedback")), "If always_cohort_inline_discussions set to False, top-level discussion are not affected.", )
def test_is_commentable_cohorted(self): course = modulestore().get_course(self.toy_course_key) self.assertFalse(cohorts.is_course_cohorted(course.id)) def to_id(name): """Helper for topic_name_to_id that uses course.""" return topic_name_to_id(course, name) # no topics self.assertFalse( utils.is_commentable_cohorted(course.id, to_id("General")), "Course doesn't even have a 'General' topic" ) # not cohorted config_course_cohorts(course, is_cohorted=False, discussion_topics=["General", "Feedback"]) self.assertFalse( utils.is_commentable_cohorted(course.id, to_id("General")), "Course isn't cohorted" ) # cohorted, but top level topics aren't config_course_cohorts(course, is_cohorted=True, discussion_topics=["General", "Feedback"]) self.assertTrue(cohorts.is_course_cohorted(course.id)) self.assertFalse( utils.is_commentable_cohorted(course.id, to_id("General")), "Course is cohorted, but 'General' isn't." ) # cohorted, including "Feedback" top-level topics aren't config_course_cohorts( course, is_cohorted=True, discussion_topics=["General", "Feedback"], cohorted_discussions=["Feedback"] ) self.assertTrue(cohorts.is_course_cohorted(course.id)) self.assertFalse( utils.is_commentable_cohorted(course.id, to_id("General")), "Course is cohorted, but 'General' isn't." ) self.assertTrue( utils.is_commentable_cohorted(course.id, to_id("Feedback")), "Feedback was listed as cohorted. Should be." )
def test_readd_to_different_cohort(self): config_course_cohorts(self.course, is_cohorted=True, manual_cohorts=["cohort1", "cohort2"]) response = self.request_bulk_enroll({ 'identifiers': self.notenrolled_student.username, 'action': 'enroll', 'email_students': False, 'courses': self.course_key, 'cohorts': "cohort1" }) assert response.status_code == 200 # test the response data expected = { "action": "enroll", 'auto_enroll': False, "email_students": False, "courses": { self.course_key: { "action": "enroll", 'auto_enroll': False, "results": [ { "identifier": self.notenrolled_student.username, "before": { "enrollment": False, "auto_enroll": False, "user": True, "allowed": False, "cohort": None, }, "after": { "enrollment": True, "auto_enroll": False, "user": True, "allowed": False, "cohort": 'cohort1', } } ] } } } manual_enrollments = ManualEnrollmentAudit.objects.all() assert manual_enrollments.count() == 1 assert manual_enrollments[0].state_transition == UNENROLLED_TO_ENROLLED res_json = json.loads(response.content.decode('utf-8')) assert get_cohort_id(self.notenrolled_student, CourseKey.from_string(self.course_key)) is not None assert res_json == expected response2 = self.request_bulk_enroll({ 'identifiers': self.notenrolled_student.username, 'action': 'enroll', 'email_students': False, 'courses': self.course_key, 'cohorts': "cohort2" }) assert response2.status_code == 200 # test the response data expected2 = { "action": "enroll", 'auto_enroll': False, "email_students": False, "courses": { self.course_key: { "action": "enroll", 'auto_enroll': False, "results": [ { "identifier": self.notenrolled_student.username, "before": { "enrollment": True, "auto_enroll": False, "user": True, "allowed": False, "cohort": 'cohort1', }, "after": { "enrollment": True, "auto_enroll": False, "user": True, "allowed": False, "cohort": 'cohort2', } } ] } } } res2_json = json.loads(response2.content.decode('utf-8')) assert get_cohort_id(self.notenrolled_student, CourseKey.from_string(self.course_key)) is not None assert res2_json == expected2