def test_add_user_to_cohort(self): """ Make sure cohorts.add_user_to_cohort() properly adds a user to a cohort and handles errors. """ course_user = UserFactory(username="******", email="*****@*****.**") UserFactory(username="******", email="*****@*****.**") course = modulestore().get_course(self.toy_course_key) CourseEnrollment.enroll(course_user, self.toy_course_key) first_cohort = CohortFactory(course_id=course.id, name="FirstCohort") second_cohort = CohortFactory(course_id=course.id, name="SecondCohort") # Success cases # We shouldn't get back a previous cohort, since the user wasn't in one self.assertEqual(cohorts.add_user_to_cohort(first_cohort, "Username"), (course_user, None)) # Should get (user, previous_cohort_name) when moved from one cohort to # another self.assertEqual(cohorts.add_user_to_cohort(second_cohort, "Username"), (course_user, "FirstCohort")) # Error cases # Should get ValueError if user already in cohort self.assertRaises( ValueError, lambda: cohorts.add_user_to_cohort(second_cohort, "Username")) # UserDoesNotExist if user truly does not exist self.assertRaises( User.DoesNotExist, lambda: cohorts.add_user_to_cohort( first_cohort, "non_existent_username"))
def test_delete_empty_cohort(self): """ Make sure that cohorts.delete_empty_cohort() properly removes an empty cohort for a given course. """ course = modulestore().get_course(self.toy_course_key) user = UserFactory(username="******", email="*****@*****.**") empty_cohort = CohortFactory(course_id=course.id, name="EmptyCohort") nonempty_cohort = CohortFactory(course_id=course.id, name="NonemptyCohort") nonempty_cohort.users.add(user) cohorts.delete_empty_cohort(course.id, "EmptyCohort") # Make sure we cannot access the deleted cohort self.assertRaises( CourseUserGroup.DoesNotExist, lambda: cohorts.get_cohort_by_id(course.id, empty_cohort.id)) self.assertRaises( ValueError, lambda: cohorts.delete_empty_cohort(course.id, "NonemptyCohort")) self.assertRaises( CourseUserGroup.DoesNotExist, lambda: cohorts.delete_empty_cohort( SlashSeparatedCourseKey('course', 'does_not', 'exist'), "EmptyCohort")) self.assertRaises( CourseUserGroup.DoesNotExist, lambda: cohorts.delete_empty_cohort( course.id, "NonExistentCohort"))
def test_get_cohort_by_id(self): """ Make sure cohorts.get_cohort_by_id() properly finds a cohort by id for a given course. """ course = modulestore().get_course(self.toy_course_key) cohort = CohortFactory(course_id=course.id, name="MyCohort") self.assertEqual(cohorts.get_cohort_by_id(course.id, cohort.id), cohort) cohort.delete() self.assertRaises( CourseUserGroup.DoesNotExist, lambda: cohorts.get_cohort_by_id(course.id, cohort.id))
def _create_cohorts(self): """Creates cohorts for testing""" self.cohort1_users = [UserFactory() for _ in range(3)] self.cohort2_users = [UserFactory() for _ in range(2)] self.cohort3_users = [UserFactory() for _ in range(2)] self.cohortless_users = [UserFactory() for _ in range(3)] self.unenrolled_users = [UserFactory() for _ in range(3)] self._enroll_users( self.cohort1_users + self.cohort2_users + self.cohort3_users + self.cohortless_users, self.course.id) self.cohort1 = CohortFactory(course_id=self.course.id, users=self.cohort1_users) self.cohort2 = CohortFactory(course_id=self.course.id, users=self.cohort2_users) self.cohort3 = CohortFactory(course_id=self.course.id, users=self.cohort3_users)
def test_enrolled_students_features_keys_cohorted(self): course = CourseFactory.create(course_key=self.course_key) course.cohort_config = {'cohorted': True, 'auto_cohort': True, 'auto_cohort_groups': ['cohort']} self.store.update_item(course, self.instructor.id) cohort = CohortFactory.create(name='cohort', course_id=course.id) cohorted_students = [UserFactory.create() for _ in xrange(10)] cohorted_usernames = [student.username for student in cohorted_students] non_cohorted_student = UserFactory.create() for student in cohorted_students: cohort.users.add(student) CourseEnrollment.enroll(student, course.id) CourseEnrollment.enroll(non_cohorted_student, course.id) instructor = InstructorFactory(course_key=course.id) self.client.login(username=instructor.username, password='******') query_features = ('username', 'cohort') # There should be a constant of 2 SQL queries when calling # enrolled_students_features. The first query comes from the call to # User.objects.filter(...), and the second comes from # prefetch_related('course_groups'). with self.assertNumQueries(2): userreports = enrolled_students_features(course.id, query_features) self.assertEqual(len([r for r in userreports if r['username'] in cohorted_usernames]), len(cohorted_students)) self.assertEqual(len([r for r in userreports if r['username'] == non_cohorted_student.username]), 1) for report in userreports: self.assertEqual(set(report.keys()), set(query_features)) if report['username'] in cohorted_usernames: self.assertEqual(report['cohort'], cohort.name) else: self.assertEqual(report['cohort'], '[unassigned]')
def test_get_cohort(self): """ Make sure cohorts.get_cohort() does the right thing when the course is cohorted """ course = modulestore().get_course(self.toy_course_key) self.assertEqual(course.id, self.toy_course_key) self.assertFalse(course.is_cohorted) user = UserFactory(username="******", email="*****@*****.**") other_user = UserFactory(username="******", email="*****@*****.**") self.assertIsNone(cohorts.get_cohort(user, course.id), "No cohort created yet") cohort = CohortFactory(course_id=course.id, name="TestCohort") cohort.users.add(user) self.assertIsNone(cohorts.get_cohort(user, course.id), "Course isn't cohorted, so shouldn't have a cohort") # Make the course cohorted... config_course_cohorts(course, discussions=[], cohorted=True) self.assertEquals( cohorts.get_cohort(user, course.id).id, cohort.id, "user should be assigned to the correct cohort") self.assertEquals( cohorts.get_cohort(other_user, course.id).id, cohorts.get_cohort_by_name(course.id, cohorts.DEFAULT_COHORT_NAME).id, "other_user should be assigned to the default cohort")
def test_get_cohort_by_id(self): """ Make sure cohorts.get_cohort_by_id() properly finds a cohort by id for a given course. """ course = modulestore().get_course(self.toy_course_key) cohort = CohortFactory(course_id=course.id, name="MyCohort") self.assertEqual(cohorts.get_cohort_by_id(course.id, cohort.id), cohort) cohort.delete() self.assertRaises( CourseUserGroup.DoesNotExist, lambda: cohorts.get_cohort_by_id(course.id, cohort.id) )
def test_cohort_membership_changed(self, mock_tracker): cohort_list = [CohortFactory() for _ in range(2)] non_cohort = CourseUserGroup.objects.create(name="dummy", course_id=self.course_key, group_type="dummy") user_list = [UserFactory() for _ in range(2)] mock_tracker.reset_mock() def assert_events(event_name_suffix, user_list, cohort_list): mock_tracker.emit.assert_has_calls([ call( "edx.cohort.user_" + event_name_suffix, { "user_id": user.id, "cohort_id": cohort.id, "cohort_name": cohort.name, }) for user in user_list for cohort in cohort_list ]) # Add users to cohort cohort_list[0].users.add(*user_list) assert_events("added", user_list, cohort_list[:1]) mock_tracker.reset_mock() # Remove users from cohort cohort_list[0].users.remove(*user_list) assert_events("removed", user_list, cohort_list[:1]) mock_tracker.reset_mock() # Clear users from cohort cohort_list[0].users.add(*user_list) cohort_list[0].users.clear() assert_events("removed", user_list, cohort_list[:1]) mock_tracker.reset_mock() # Clear users from non-cohort group non_cohort.users.add(*user_list) non_cohort.users.clear() self.assertFalse(mock_tracker.emit.called) # Add cohorts to user user_list[0].course_groups.add(*cohort_list) assert_events("added", user_list[:1], cohort_list) mock_tracker.reset_mock() # Remove cohorts from user user_list[0].course_groups.remove(*cohort_list) assert_events("removed", user_list[:1], cohort_list) mock_tracker.reset_mock() # Clear cohorts from user user_list[0].course_groups.add(*cohort_list) user_list[0].course_groups.clear() assert_events("removed", user_list[:1], cohort_list) mock_tracker.reset_mock() # Clear non-cohort groups from user user_list[0].course_groups.add(non_cohort) user_list[0].course_groups.clear() self.assertFalse(mock_tracker.emit.called)
def test_non_staff(self): """ Verify that non-staff users cannot access `check_users_in_cohort`. """ cohort = CohortFactory(course_id=self.course.id, users=[]) self._verify_non_staff_cannot_access( remove_user_from_cohort, "POST", [self.course.id.to_deprecated_string(), cohort.id])
def test_get_course_cohorts(self): """ Tests that get_course_cohorts returns all cohorts, including auto cohorts. """ course = modulestore().get_course(self.toy_course_key) config_course_cohorts(course, [], cohorted=True, auto_cohort_groups=["AutoGroup1", "AutoGroup2"]) # add manual cohorts to course 1 CohortFactory(course_id=course.id, name="ManualCohort") CohortFactory(course_id=course.id, name="ManualCohort2") cohort_set = {c.name for c in cohorts.get_course_cohorts(course)} self.assertEqual( cohort_set, {"AutoGroup1", "AutoGroup2", "ManualCohort", "ManualCohort2"})
def test_can_remove_user_from_cohort(self): """ Verify that we can remove a user from a cohort. """ user = UserFactory() cohort = CohortFactory(course_id=self.course.id, users=[user]) response_dict = self.request_remove_user_from_cohort( user.username, cohort) self.verify_removed_user_from_cohort(user.username, response_dict, cohort)
def test_can_remove_user_not_in_cohort(self): """ Verify that we can "remove" a user from a cohort even if they are not a member of that cohort. """ user = UserFactory() cohort = CohortFactory(course_id=self.course.id, users=[]) response_dict = self.request_remove_user_from_cohort( user.username, cohort) self.verify_removed_user_from_cohort(user.username, response_dict, cohort)
def test_no_username_given(self): """ Verify that we get an error message when omitting a username. """ cohort = CohortFactory(course_id=self.course.id, users=[]) response_dict = self.request_remove_user_from_cohort(None, cohort) self.verify_removed_user_from_cohort( None, response_dict, cohort, expected_error_msg='No username specified')
def test_no_users(self): """ Verify that we don't get back any users for a cohort with no users. """ cohort = CohortFactory(course_id=self.course.id, users=[]) response_dict = self.request_users_in_cohort(cohort, self.course, 1) self.verify_users_in_cohort_and_response(cohort, response_dict, expected_users=[], expected_page=1, expected_num_pages=1)
def test_user_does_not_exist(self): """ Verify that we get an error message when the requested user to remove does not exist. """ username = "******" cohort = CohortFactory(course_id=self.course.id, users=[]) response_dict = self.request_remove_user_from_cohort(username, cohort) self.verify_removed_user_from_cohort( username, response_dict, cohort, expected_error_msg='No user \'{0}\''.format(username))
def test_few_users(self): """ Verify that we get back all users for a cohort when the cohort has <=100 users. """ users = [UserFactory() for _ in range(5)] cohort = CohortFactory(course_id=self.course.id, users=users) response_dict = self.request_users_in_cohort(cohort, self.course, 1) self.verify_users_in_cohort_and_response(cohort, response_dict, expected_users=users, expected_page=1, expected_num_pages=1)
def test_non_positive_page(self): """ Verify that we get a `HttpResponseBadRequest` (bad request) when the page we request isn't a positive integer. """ users = [UserFactory() for _ in range(5)] cohort = CohortFactory(course_id=self.course.id, users=users) self.request_users_in_cohort(cohort, self.course, "invalid", should_return_bad_request=True) self.request_users_in_cohort(cohort, self.course, -1, should_return_bad_request=True)
def test_non_existent_cohort(self): """ Verify that an error is raised when trying to add users to a cohort which does not belong to the given course. """ users = [UserFactory(username="******".format(i)) for i in range(3)] usernames = [user.username for user in users] wrong_course_key = SlashSeparatedCourseKey("some", "arbitrary", "course") wrong_course_cohort = CohortFactory(name="wrong_cohort", course_id=wrong_course_key, users=[]) self.request_add_users_to_cohort(",".join(usernames), wrong_course_cohort, self.course, should_raise_404=True)
def test_many_users(self): """ Verify that pagination works correctly for cohorts with >100 users. """ users = [UserFactory() for _ in range(101)] cohort = CohortFactory(course_id=self.course.id, users=users) response_dict_1 = self.request_users_in_cohort(cohort, self.course, 1) response_dict_2 = self.request_users_in_cohort(cohort, self.course, 2) self.verify_users_in_cohort_and_response(cohort, response_dict_1, expected_users=users[:100], expected_page=1, expected_num_pages=2) self.verify_users_in_cohort_and_response(cohort, response_dict_2, expected_users=users[100:], expected_page=2, expected_num_pages=2)
def test_enrolled_students_features_keys_cohorted(self): course = CourseFactory.create(course_key=self.course_key) course.cohort_config = { 'cohorted': True, 'auto_cohort': True, 'auto_cohort_groups': ['cohort'] } self.store.update_item(course, self.instructor.id) cohort = CohortFactory.create(name='cohort', course_id=course.id) cohorted_students = [UserFactory.create() for _ in xrange(10)] cohorted_usernames = [ student.username for student in cohorted_students ] non_cohorted_student = UserFactory.create() for student in cohorted_students: cohort.users.add(student) CourseEnrollment.enroll(student, course.id) CourseEnrollment.enroll(non_cohorted_student, course.id) instructor = InstructorFactory(course_key=course.id) self.client.login(username=instructor.username, password='******') query_features = ('username', 'cohort') # There should be a constant of 2 SQL queries when calling # enrolled_students_features. The first query comes from the call to # User.objects.filter(...), and the second comes from # prefetch_related('course_groups'). with self.assertNumQueries(2): userreports = enrolled_students_features(course.id, query_features) self.assertEqual( len([ r for r in userreports if r['username'] in cohorted_usernames ]), len(cohorted_students)) self.assertEqual( len([ r for r in userreports if r['username'] == non_cohorted_student.username ]), 1) for report in userreports: self.assertEqual(set(report.keys()), set(query_features)) if report['username'] in cohorted_usernames: self.assertEqual(report['cohort'], cohort.name) else: self.assertEqual(report['cohort'], '[unassigned]')
def test_out_of_range(self): """ Verify that we get a blank page of users when requesting page 0 or a page greater than the actual number of pages. """ users = [UserFactory() for _ in range(5)] cohort = CohortFactory(course_id=self.course.id, users=users) response = self.request_users_in_cohort(cohort, self.course, 0) self.verify_users_in_cohort_and_response(cohort, response, expected_users=[], expected_page=0, expected_num_pages=1) response = self.request_users_in_cohort(cohort, self.course, 2) self.verify_users_in_cohort_and_response(cohort, response, expected_users=[], expected_page=2, expected_num_pages=1)
def test_get_cohort_id(self): """ Make sure that cohorts.get_cohort_id() correctly returns the cohort id, or raises a ValueError when given an invalid course key. """ course = modulestore().get_course(self.toy_course_key) self.assertFalse(course.is_cohorted) user = UserFactory(username="******", email="*****@*****.**") self.assertIsNone(cohorts.get_cohort_id(user, course.id)) config_course_cohorts(course, discussions=[], cohorted=True) cohort = CohortFactory(course_id=course.id, name="TestCohort") cohort.users.add(user) self.assertEqual(cohorts.get_cohort_id(user, course.id), cohort.id) self.assertRaises( ValueError, lambda: cohorts.get_cohort_id( user, SlashSeparatedCourseKey("course", "does_not", "exist")))
def test_get_cohort_by_name(self): """ Make sure cohorts.get_cohort_by_name() properly finds a cohort by name for a given course. Also verify that it raises an error when the cohort is not found. """ course = modulestore().get_course(self.toy_course_key) self.assertRaises( CourseUserGroup.DoesNotExist, lambda: cohorts.get_cohort_by_name( course.id, "CohortDoesNotExist")) cohort = CohortFactory(course_id=course.id, name="MyCohort") self.assertEqual(cohorts.get_cohort_by_name(course.id, "MyCohort"), cohort) self.assertRaises( CourseUserGroup.DoesNotExist, lambda: cohorts.get_cohort_by_name( SlashSeparatedCourseKey("course", "does_not", "exist"), cohort) )
def test_auto_cohorting(self): """ Make sure cohorts.get_cohort() does the right thing with auto_cohort_groups """ course = modulestore().get_course(self.toy_course_key) self.assertFalse(course.is_cohorted) user1 = UserFactory(username="******", email="*****@*****.**") user2 = UserFactory(username="******", email="*****@*****.**") user3 = UserFactory(username="******", email="*****@*****.**") user4 = UserFactory(username="******", email="*****@*****.**") cohort = CohortFactory(course_id=course.id, name="TestCohort") # user1 manually added to a cohort cohort.users.add(user1) # Add an auto_cohort_group to the course... config_course_cohorts(course, discussions=[], cohorted=True, auto_cohort_groups=["AutoGroup"]) self.assertEquals( cohorts.get_cohort(user1, course.id).id, cohort.id, "user1 should stay put") self.assertEquals( cohorts.get_cohort(user2, course.id).name, "AutoGroup", "user2 should be auto-cohorted") # Now make the auto_cohort_group list empty config_course_cohorts(course, discussions=[], cohorted=True, auto_cohort_groups=[]) self.assertEquals( cohorts.get_cohort(user3, course.id).id, cohorts.get_cohort_by_name(course.id, cohorts.DEFAULT_COHORT_NAME).id, "No groups->default cohort") # Now set the auto_cohort_group to something different config_course_cohorts(course, discussions=[], cohorted=True, auto_cohort_groups=["OtherGroup"]) self.assertEquals( cohorts.get_cohort(user4, course.id).name, "OtherGroup", "New list->new group") self.assertEquals( cohorts.get_cohort(user1, course.id).name, "TestCohort", "user1 should still be in originally placed cohort") self.assertEquals( cohorts.get_cohort(user2, course.id).name, "AutoGroup", "user2 should still be in originally placed cohort") self.assertEquals( cohorts.get_cohort(user3, course.id).name, cohorts.get_cohort_by_name(course.id, cohorts.DEFAULT_COHORT_NAME).name, "user3 should still be in the default cohort")