def setup_course(self, default_store=None): """ Helper method to create the course. """ if not default_store: default_store = self.store.default_modulestore.get_modulestore_type() with self.store.default_store(default_store): self.course = CourseFactory.create(**self.course_options()) chapter = ItemFactory.create(parent=self.course, category='chapter') self.vertical_block = ItemFactory.create( parent_location=chapter.location, category='vertical', display_name="Vertical" ) self.html_block = ItemFactory.create( parent=self.vertical_block, category='html', data="<p>Test HTML Content<p>" ) CourseOverview.load_from_module_store(self.course.id) # block_name_to_be_tested can be `html_block` or `vertical_block`. # These attributes help ensure the positive and negative tests are in sync. self.block_to_be_tested = getattr(self, self.block_name_to_be_tested) self.block_specific_chrome_html_elements = self.BLOCK_SPECIFIC_CHROME_HTML_ELEMENTS[ self.block_name_to_be_tested ]
def setUp(self): """ Create a test course. """ super(AdminCourseModeFormTest, self).setUp() self.course = CourseFactory.create() CourseOverview.load_from_module_store(self.course.id)
def test_with_org_and_invalid_to_mode(self, mock_tracker): """Verify that enrollments are changed correctly when org was given.""" from_mode = 'audit' to_mode = 'no-id-professional' self._enroll_users(self.course, self.users, from_mode) # Create a second course under the same org course_2 = CourseFactory.create(org=self.org) CourseModeFactory(course_id=course_2.id, mode_slug=to_mode) CourseOverview.load_from_module_store(course_2.id) self._enroll_users(course_2, self.users, from_mode) # Verify that no users are in the `to` mode yet. self.assertEqual(len(CourseEnrollment.objects.filter(mode=to_mode, course_id=self.course.id)), 0) self.assertEqual(len(CourseEnrollment.objects.filter(mode=to_mode, course_id=course_2.id)), 0) call_command( 'bulk_change_enrollment', org=self.org, from_mode=from_mode, to_mode=to_mode, commit=True, ) # Verify that users were not moved for the invalid course/mode combination for user in self.users: with self.assertRaises(CourseEnrollment.DoesNotExist): CourseEnrollment.objects.get(mode=to_mode, course_id=self.course.id, user=user) # Verify that all users have been moved -- if not, this will # raise CourseEnrollment.DoesNotExist for user in self.users: CourseEnrollment.objects.get(mode=to_mode, course_id=course_2.id, user=user) self._assert_mode_changed(mock_tracker, course_2, user, to_mode)
def test_bulk_convert_with_org(self, from_mode, to_mode, mock_tracker): """Verify that enrollments are changed correctly when org was given.""" self._enroll_users(self.course, self.users, from_mode) CourseModeFactory(course_id=self.course.id, mode_slug=to_mode) # Create a second course under the same org course_2 = CourseFactory.create(org=self.org) CourseModeFactory(course_id=course_2.id, mode_slug=to_mode) CourseOverview.load_from_module_store(course_2.id) self._enroll_users(course_2, self.users, from_mode) # Verify that no users are in the `to` mode yet. self.assertEqual(len(CourseEnrollment.objects.filter(mode=to_mode, course_id=self.course.id)), 0) self.assertEqual(len(CourseEnrollment.objects.filter(mode=to_mode, course_id=course_2.id)), 0) args = '--org {org} --from_mode {from_mode} --to_mode {to_mode} --commit'.format( org=self.org, from_mode=from_mode, to_mode=to_mode ) call_command( 'bulk_change_enrollment', *args.split(' ') ) # Verify that all users have been moved -- if not, this will # raise CourseEnrollment.DoesNotExist for user in self.users: for course in [self.course, course_2]: CourseEnrollment.objects.get(mode=to_mode, course_id=course.id, user=user) self._assert_mode_changed(mock_tracker, course, user, to_mode)
def test_bulk_convert_with_org(self, from_mode, to_mode, mock_tracker): """Verify that enrollments are changed correctly when org was given.""" self._enroll_users(self.course, self.users, from_mode) CourseModeFactory(course_id=self.course.id, mode_slug=to_mode) # Create a second course under the same org course_2 = CourseFactory.create(org=self.org) CourseModeFactory(course_id=course_2.id, mode_slug=to_mode) CourseOverview.load_from_module_store(course_2.id) self._enroll_users(course_2, self.users, from_mode) # Verify that no users are in the `to` mode yet. self.assertEqual(len(CourseEnrollment.objects.filter(mode=to_mode, course_id=self.course.id)), 0) self.assertEqual(len(CourseEnrollment.objects.filter(mode=to_mode, course_id=course_2.id)), 0) call_command( 'bulk_change_enrollment', org=self.org, from_mode=from_mode, to_mode=to_mode, commit=True, ) # Verify that all users have been moved -- if not, this will # raise CourseEnrollment.DoesNotExist for user in self.users: for course in [self.course, course_2]: CourseEnrollment.objects.get(mode=to_mode, course_id=course.id, user=user) self._assert_mode_changed(mock_tracker, course, user, to_mode)
def setup_course(self, default_store=None): """ Helper method to create the course. """ if not default_store: default_store = self.store.default_modulestore.get_modulestore_type() with self.store.default_store(default_store): self.course = CourseFactory.create(**self.course_options()) chapter = ItemFactory.create(parent=self.course, category='chapter') self.vertical_block = ItemFactory.create( parent_location=chapter.location, category='vertical', display_name="Vertical" ) self.html_block = ItemFactory.create( parent=self.vertical_block, category='html', data="<p>Test HTML Content<p>" ) self.problem_block = ItemFactory.create( parent=self.vertical_block, category='problem', display_name='Problem' ) CourseOverview.load_from_module_store(self.course.id) # block_name_to_be_tested can be `html_block` or `vertical_block`. # These attributes help ensure the positive and negative tests are in sync. self.block_to_be_tested = getattr(self, self.block_name_to_be_tested) self.block_specific_chrome_html_elements = self.BLOCK_SPECIFIC_CHROME_HTML_ELEMENTS[ self.block_name_to_be_tested ]
def setUp(self): """ Create a test course. """ super().setUp() self.course = CourseFactory.create() CourseOverview.load_from_module_store(self.course.id)
def setUp(self): """ Create a test course. """ super(AdminCourseModeFormTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments self.course = CourseFactory.create() CourseOverview.load_from_module_store(self.course.id)
def setUpClass(cls): super().setUpClass() cls._course = course = CourseFactory.create(enable_ccx=True) CourseOverview.load_from_module_store(course.id) # Create a course outline cls.mooc_start = start = datetime.datetime( 2010, 5, 12, 2, 42, tzinfo=UTC ) chapter = ItemFactory.create( start=start, parent=course, category='sequential' ) cls.sections = sections = [ ItemFactory.create( parent=chapter, category="sequential", metadata={'graded': True, 'format': 'Homework'}) for _ in range(4) ] # making problems available at class level for possible future use in tests cls.problems = [ [ ItemFactory.create( parent=section, category="problem", data=StringResponseXMLFactory().build_xml(answer='foo'), metadata={'rerandomize': 'always'} ) for _ in range(4) ] for section in sections ]
def test_expiration_timezone(self): # Test that expiration datetimes are saved and retrieved with the timezone set to UTC. # This verifies the fix for a bug in which the date displayed to users was different # than the date in Django admin. user = UserFactory.create(is_staff=True, is_superuser=True) user.save() course = CourseFactory.create() expiration = datetime(2015, 10, 20, 1, 10, 23, tzinfo=timezone(settings.TIME_ZONE)) CourseOverview.load_from_module_store(course.id) data = { 'course': str(course.id), 'mode_slug': 'verified', 'mode_display_name': 'verified', 'min_price': 10, 'currency': 'usd', '_expiration_datetime_0': expiration.date( ), # due to django admin datetime widget passing as separate vals '_expiration_datetime_1': expiration.time(), } self.client.login(username=user.username, password='******') # Create a new course mode from django admin page response = self.client.post( reverse('admin:course_modes_coursemode_add'), data=data) self.assertRedirects( response, reverse('admin:course_modes_coursemode_changelist')) course_mode = CourseMode.objects.get(course_id=str(course.id), mode_slug='verified') # Verify that datetime is appears on list page response = self.client.get( reverse('admin:course_modes_coursemode_changelist')) self.assertContains( response, get_time_display(expiration, '%B %d, %Y, %H:%M %p')) # Verify that on the edit page the datetime value appears as UTC. resp = self.client.get( reverse('admin:course_modes_coursemode_change', args=(course_mode.id, ))) self.assertContains(resp, expiration.date()) self.assertContains(resp, expiration.time()) # Verify that the expiration datetime is the same as what we set # (hasn't changed because of a timezone translation). course_mode.refresh_from_db() assert course_mode.expiration_datetime.replace( tzinfo=None) == expiration.replace(tzinfo=None)
def test_enrollments_not_deleted(self): """ Recreating a CourseOverview with an outdated version should not delete the associated enrollment. """ course = CourseFactory(self_paced=True) CourseModeFactory( course_id=course.id, mode_slug=CourseMode.VERIFIED, # This must be in the future to ensure it is returned by downstream code. expiration_datetime=datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=30), ) # Create a CourseOverview with an outdated version course_overview = CourseOverview.load_from_module_store(course.id) course_overview.version = CourseOverview.VERSION - 1 course_overview.save() # Create an inactive enrollment with this course overview enrollment = CourseEnrollmentFactory( user=self.user, course_id=course.id, mode=CourseMode.AUDIT, course=course_overview, ) # Re-fetch the CourseOverview record. # As a side effect, this will recreate the record, and update the version. course_overview_new = CourseOverview.get_from_id(course.id) self.assertEqual(course_overview_new.version, CourseOverview.VERSION) # Ensure that the enrollment record was unchanged during this re-creation enrollment_refetched = CourseEnrollment.objects.filter( id=enrollment.id) self.assertTrue(enrollment_refetched.exists()) self.assertEqual(enrollment_refetched.all()[0], enrollment)
def handle(self, *args, **options): courses = modulestore().get_courses() # Find the largest auto-generated course, and pick the next sequence id to generate the next # course with. max_org_sequence_id = max([0] + [ int(course.org[4:]) for course in courses if course.org.startswith('org.') ]) XMODULE_FACTORY_LOCK.enable() CourseFactory.reset_sequence(max_org_sequence_id + 1, force=True) course = CourseFactory.create( start=datetime.datetime.today() - datetime.timedelta(days=30), end=datetime.datetime.today() + datetime.timedelta(days=30), number=factory.Sequence('schedules_test_course_{}'.format), display_name=factory.Sequence(u'Schedules Test Course {}'.format), ) XMODULE_FACTORY_LOCK.disable() course_overview = CourseOverview.load_from_module_store(course.id) CourseModeFactory.create(course_id=course_overview.id, mode_slug=CourseMode.AUDIT) CourseModeFactory.create(course_id=course_overview.id, mode_slug=CourseMode.VERIFIED) CourseDurationLimitExpirySchedule.create_batch( 20, enrollment__course=course_overview) ScheduleConfigFactory.create(site=Site.objects.get(name='example.com'))
def handle(self, *args, **options): courses = modulestore().get_courses() # Find the largest auto-generated course, and pick the next sequence id to generate the next # course with. max_org_sequence_id = max( int(course.org[4:]) for course in courses if course.org.startswith('org.')) XMODULE_FACTORY_LOCK.enable() CourseFactory.reset_sequence(max_org_sequence_id + 1, force=True) course = CourseFactory.create( start=datetime.datetime.today() - datetime.timedelta(days=30), end=datetime.datetime.today() + datetime.timedelta(days=30), number=factory.Sequence('schedules_test_course_{}'.format), display_name=factory.Sequence('Schedules Test Course {}'.format), ) XMODULE_FACTORY_LOCK.disable() course_overview = CourseOverview.load_from_module_store(course.id) ThreeDayNudgeSchedule.create(enrollment__course=course_overview) TenDayNudgeSchedule.create(enrollment__course=course_overview) UpgradeReminderSchedule.create(enrollment__course=course_overview) ContentHighlightSchedule.create(enrollment__course=course_overview) ScheduleConfigFactory.create(site=Site.objects.get(name='example.com'))
def test_enrollments_not_deleted(self): """ Recreating a CourseOverview with an outdated version should not delete the associated enrollment. """ course = CourseFactory(self_paced=True) CourseModeFactory( course_id=course.id, mode_slug=CourseMode.VERIFIED, # This must be in the future to ensure it is returned by downstream code. expiration_datetime=datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=30), ) # Create a CourseOverview with an outdated version course_overview = CourseOverview.load_from_module_store(course.id) course_overview.version = CourseOverview.VERSION - 1 course_overview.save() # Create an inactive enrollment with this course overview enrollment = CourseEnrollmentFactory( user=self.user, course_id=course.id, mode=CourseMode.AUDIT, course=course_overview, ) # Re-fetch the CourseOverview record. # As a side effect, this will recreate the record, and update the version. course_overview_new = CourseOverview.get_from_id(course.id) self.assertEqual(course_overview_new.version, CourseOverview.VERSION) # Ensure that the enrollment record was unchanged during this re-creation enrollment_refetched = CourseEnrollment.objects.filter(id=enrollment.id) self.assertTrue(enrollment_refetched.exists()) self.assertEqual(enrollment_refetched.all()[0], enrollment)
def setUp(self): """ Set up tests """ super().setUp() # Create instructor account self.coach = coach = AdminFactory.create() self.client.login(username=coach.username, password="******") # Create CCX role = CourseCcxCoachRole(self._course.id) role.add_users(coach) ccx = CcxFactory(course_id=self._course.id, coach=self.coach) # override course grading policy and make last section invisible to students override_field_for_ccx(ccx, self._course, 'grading_policy', { 'GRADER': [ {'drop_count': 0, 'min_count': 2, 'short_label': 'HW', 'type': 'Homework', 'weight': 1} ], 'GRADE_CUTOFFS': {'Pass': 0.75}, }) override_field_for_ccx( ccx, self.sections[-1], 'visible_to_staff_only', True ) # create a ccx locator and retrieve the course structure using that key # which emulates how a student would get access. self.ccx_key = CCXLocator.from_course_locator(self._course.id, str(ccx.id)) self.course = get_course_by_id(self.ccx_key, depth=None) CourseOverview.load_from_module_store(self.course.id) setup_students_and_grades(self) self.client.login(username=coach.username, password="******") self.addCleanup(RequestCache.clear_all_namespaces) from xmodule.modulestore.django import SignalHandler # using CCX object as sender here. SignalHandler.course_published.send( sender=ccx, course_key=self.ccx_key )
def test_with_org_and_invalid_to_mode(self, mock_tracker): """Verify that enrollments are changed correctly when org was given.""" from_mode = 'audit' to_mode = 'no-id-professional' self._enroll_users(self.course, self.users, from_mode) # Create a second course under the same org course_2 = CourseFactory.create(org=self.org) CourseModeFactory(course_id=course_2.id, mode_slug=to_mode) CourseOverview.load_from_module_store(course_2.id) self._enroll_users(course_2, self.users, from_mode) # Verify that no users are in the `to` mode yet. self.assertEqual( len( CourseEnrollment.objects.filter(mode=to_mode, course_id=self.course.id)), 0) self.assertEqual( len( CourseEnrollment.objects.filter(mode=to_mode, course_id=course_2.id)), 0) call_command( 'bulk_change_enrollment', org=self.org, from_mode=from_mode, to_mode=to_mode, commit=True, ) # Verify that users were not moved for the invalid course/mode combination for user in self.users: with self.assertRaises(CourseEnrollment.DoesNotExist): CourseEnrollment.objects.get(mode=to_mode, course_id=self.course.id, user=user) # Verify that all users have been moved -- if not, this will # raise CourseEnrollment.DoesNotExist for user in self.users: CourseEnrollment.objects.get(mode=to_mode, course_id=course_2.id, user=user) self._assert_mode_changed(mock_tracker, course_2, user, to_mode)
def test_expiration_timezone(self): # Test that expiration datetimes are saved and retrieved with the timezone set to UTC. # This verifies the fix for a bug in which the date displayed to users was different # than the date in Django admin. user = UserFactory.create(is_staff=True, is_superuser=True) user.save() course = CourseFactory.create() expiration = datetime(2015, 10, 20, 1, 10, 23, tzinfo=timezone(settings.TIME_ZONE)) CourseOverview.load_from_module_store(course.id) data = { 'course': unicode(course.id), 'mode_slug': 'verified', 'mode_display_name': 'verified', 'min_price': 10, 'currency': 'usd', '_expiration_datetime_0': expiration.date(), # due to django admin datetime widget passing as separate vals '_expiration_datetime_1': expiration.time(), } self.client.login(username=user.username, password='******') # Create a new course mode from django admin page response = self.client.post(reverse('admin:course_modes_coursemode_add'), data=data) self.assertRedirects(response, reverse('admin:course_modes_coursemode_changelist')) # Verify that datetime is appears on list page response = self.client.get(reverse('admin:course_modes_coursemode_changelist')) self.assertContains(response, get_time_display(expiration, '%B %d, %Y, %H:%M %p')) # Verify that on the edit page the datetime value appears as UTC. resp = self.client.get(reverse('admin:course_modes_coursemode_change', args=(1,))) self.assertContains(resp, expiration.date()) self.assertContains(resp, expiration.time()) # Verify that the expiration datetime is the same as what we set # (hasn't changed because of a timezone translation). course_mode = CourseMode.objects.get(pk=1) self.assertEqual(course_mode.expiration_datetime.replace(tzinfo=None), expiration.replace(tzinfo=None))
def test_upgrade_deadline_with_schedule(self): """ The property should use either the CourseMode or related Schedule to determine the deadline. """ course = CourseFactory(self_paced=True) CourseModeFactory( course_id=course.id, mode_slug=CourseMode.VERIFIED, # This must be in the future to ensure it is returned by downstream code. expiration_datetime=datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=30), ) course_overview = CourseOverview.load_from_module_store(course.id) enrollment = CourseEnrollmentFactory( course_id=course.id, mode=CourseMode.AUDIT, course=course_overview, ) # The schedule's upgrade deadline should be used if a schedule exists DynamicUpgradeDeadlineConfiguration.objects.create(enabled=True) schedule = ScheduleFactory(enrollment=enrollment) self.assertEqual(enrollment.upgrade_deadline, schedule.upgrade_deadline)
def test_upgrade_deadline_with_schedule(self): """ The property should use either the CourseMode or related Schedule to determine the deadline. """ course = CourseFactory(self_paced=True) CourseModeFactory( course_id=course.id, mode_slug=CourseMode.VERIFIED, # This must be in the future to ensure it is returned by downstream code. expiration_datetime=datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=30), ) course_overview = CourseOverview.load_from_module_store(course.id) enrollment = CourseEnrollmentFactory( course_id=course.id, mode=CourseMode.AUDIT, course=course_overview, ) # The schedule's upgrade deadline should be used if a schedule exists DynamicUpgradeDeadlineConfiguration.objects.create(enabled=True) schedule = ScheduleFactory(enrollment=enrollment) self.assertEqual(enrollment.upgrade_deadline, schedule.upgrade_deadline)
def handle(self, *args, **options): courses = modulestore().get_courses() # Find the largest auto-generated course, and pick the next sequence id to generate the next # course with. max_org_sequence_id = max([0] + [int(course.org[4:]) for course in courses if course.org.startswith('org.')]) XMODULE_FACTORY_LOCK.enable() CourseFactory.reset_sequence(max_org_sequence_id + 1, force=True) course = CourseFactory.create( start=datetime.datetime.today() - datetime.timedelta(days=30), end=datetime.datetime.today() + datetime.timedelta(days=30), number=factory.Sequence('schedules_test_course_{}'.format), display_name=factory.Sequence(u'Schedules Test Course {}'.format), ) XMODULE_FACTORY_LOCK.disable() course_overview = CourseOverview.load_from_module_store(course.id) CourseModeFactory.create(course_id=course_overview.id, mode_slug=CourseMode.AUDIT) CourseModeFactory.create(course_id=course_overview.id, mode_slug=CourseMode.VERIFIED) CourseDurationLimitExpirySchedule.create_batch(20, enrollment__course=course_overview) ScheduleConfigFactory.create(site=Site.objects.get(name='example.com'))
def handle(self, *args, **options): courses = modulestore().get_courses() # Find the largest auto-generated course, and pick the next sequence id to generate the next # course with. max_org_sequence_id = max(int(course.org[4:]) for course in courses if course.org.startswith('org.')) XMODULE_FACTORY_LOCK.enable() CourseFactory.reset_sequence(max_org_sequence_id + 1, force=True) course = CourseFactory.create( start=datetime.datetime.today() - datetime.timedelta(days=30), end=datetime.datetime.today() + datetime.timedelta(days=30), number=factory.Sequence('schedules_test_course_{}'.format), display_name=factory.Sequence('Schedules Test Course {}'.format), ) XMODULE_FACTORY_LOCK.disable() course_overview = CourseOverview.load_from_module_store(course.id) ThreeDayNudgeSchedule.create(enrollment__course=course_overview) TenDayNudgeSchedule.create(enrollment__course=course_overview) UpgradeReminderSchedule.create(enrollment__course=course_overview) ContentHighlightSchedule.create(enrollment__course=course_overview) ScheduleConfigFactory.create(site=Site.objects.get(name='example.com'))
def setUp(self): super(BulkChangeEnrollmentTests, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments self.org = 'testX' self.course = CourseFactory.create(org=self.org) self.users = UserFactory.create_batch(5) CourseOverview.load_from_module_store(self.course.id)
def setUp(self): super(BulkChangeEnrollmentTests, self).setUp() self.org = 'testX' self.course = CourseFactory.create(org=self.org) self.users = UserFactory.create_batch(5) CourseOverview.load_from_module_store(self.course.id)
def update_course_and_overview(self): self.update_course(self.course, self.user.id) CourseOverview.load_from_module_store(self.course.id)
def setUp(self): super(BulkChangeEnrollmentTests, self).setUp() self.org = 'testX' self.course = CourseFactory.create(org=self.org) self.users = UserFactory.create_batch(5) CourseOverview.load_from_module_store(self.course.id)