示例#1
0
    def test_set_status_non_credit(self):
        """
        assert that we can still try to update a credit status but return quickly if
        a course is not credit eligible
        """

        no_credit_course = CourseFactory.create(org='NoCredit', number='NoCredit', display_name='Demo_Course')

        self.assertFalse(self.service.is_credit_course(no_credit_course.id))

        CourseEnrollment.enroll(self.user, no_credit_course.id)

        # this should be a no-op
        self.service.set_credit_requirement_status(
            self.user.id,
            no_credit_course.id,
            'grade',
            'grade'
        )

        credit_state = self.service.get_credit_state(self.user.id, no_credit_course.id)

        self.assertIsNotNone(credit_state)
        self.assertFalse(credit_state['is_credit_course'])
        self.assertEqual(len(credit_state['credit_requirement_status']), 0)
示例#2
0
    def test_dashboard_metadata_caching(self, modulestore_type):
        """
        Check that the student dashboard makes use of course metadata caching.

        After creating a course, that course's metadata should be cached as a
        CourseOverview. The student dashboard should never have to make calls to
        the modulestore.

        Arguments:
            modulestore_type (ModuleStoreEnum.Type): Type of modulestore to create
                test course in.

        Note to future developers:
            If you break this test so that the "check_mongo_calls(0)" fails,
            please do NOT change it to "check_mongo_calls(n>1)". Instead, change
            your code to not load courses from the module store. This may
            involve adding fields to CourseOverview so that loading a full
            CourseDescriptor isn't necessary.
        """
        # Create a course and log in the user.
        # Creating a new course will trigger a publish event and the course will be cached
        test_course = CourseFactory.create(default_store=modulestore_type, emit_signals=True)
        self.client.login(username="******", password="******")

        with check_mongo_calls(0):
            CourseEnrollment.enroll(self.user, test_course.id)

        # Subsequent requests will only result in SQL queries to load the
        # CourseOverview object that has been created.
        with check_mongo_calls(0):
            response_1 = self.client.get(reverse('dashboard'))
            self.assertEquals(response_1.status_code, 200)
            response_2 = self.client.get(reverse('dashboard'))
            self.assertEquals(response_2.status_code, 200)
示例#3
0
 def test_roundtrip_with_unicode_course_id(self):
     course2 = CourseFactory.create(display_name=u"Omega Course Ω")
     CourseEnrollment.enroll(self.user, course2.id)
     anonymous_id = anonymous_id_for_user(self.user, course2.id)
     real_user = user_by_anonymous_id(anonymous_id)
     self.assertEqual(self.user, real_user)
     self.assertEqual(anonymous_id, anonymous_id_for_user(self.user, course2.id, save=False))
示例#4
0
    def setUp(self):
        """ Create a course and user, then log in. """
        super(BulkEnrollmentTest, self).setUp()

        self.view = BulkEnrollView.as_view()
        self.request_factory = APIRequestFactory()
        self.url = reverse('bulk_enroll')

        self.staff = UserFactory.create(
            username=self.USERNAME,
            email=self.EMAIL,
            password=self.PASSWORD,
            is_staff=True,
        )

        self.course = CourseFactory.create()
        self.course_key = unicode(self.course.id)
        self.enrolled_student = UserFactory(username='******', first_name='Enrolled', last_name='Student')
        CourseEnrollment.enroll(
            self.enrolled_student,
            self.course.id
        )
        self.notenrolled_student = UserFactory(username='******', first_name='NotEnrolled',
                                               last_name='Student')

        # Email URL values
        self.site_name = microsite.get_value(
            'SITE_NAME',
            settings.SITE_NAME
        )
        self.about_path = '/courses/{}/about'.format(self.course.id)
        self.course_path = '/courses/{}/'.format(self.course.id)
示例#5
0
    def test_linked_in_add_to_profile_btn_not_appearing_without_config(self):
        # Without linked-in config don't show Add Certificate to LinkedIn button
        self.client.login(username="******", password="******")

        CourseModeFactory.create(
            course_id=self.course.id,
            mode_slug='verified',
            mode_display_name='verified',
            expiration_datetime=datetime.now(pytz.UTC) - timedelta(days=1)
        )

        CourseEnrollment.enroll(self.user, self.course.id, mode='honor')

        self.course.start = datetime.now(pytz.UTC) - timedelta(days=2)
        self.course.end = datetime.now(pytz.UTC) - timedelta(days=1)
        self.course.display_name = u"Omega"
        self.course = self.update_course(self.course, self.user.id)

        download_url = 'www.edx.org'
        GeneratedCertificateFactory.create(
            user=self.user,
            course_id=self.course.id,
            status=CertificateStatuses.downloadable,
            mode='honor',
            grade='67',
            download_url=download_url
        )
        response = self.client.get(reverse('dashboard'))

        self.assertEquals(response.status_code, 200)
        self.assertNotIn('Add Certificate to LinkedIn', response.content)

        response_url = 'http://www.linkedin.com/profile/add?_ed='
        self.assertNotContains(response, escape(response_url))
    def test_transfer_students(self):
        student = UserFactory()
        student.set_password(self.PASSWORD)  # pylint: disable=E1101
        student.save()   # pylint: disable=E1101

        # Original Course
        original_course_location = locator.CourseLocator('Org0', 'Course0', 'Run0')
        course = self._create_course(original_course_location)
        # Enroll the student in 'verified'
        CourseEnrollment.enroll(student, course.id, mode="verified")

        # New Course 1
        course_location_one = locator.CourseLocator('Org1', 'Course1', 'Run1')
        new_course_one = self._create_course(course_location_one)

        # New Course 2
        course_location_two = locator.CourseLocator('Org2', 'Course2', 'Run2')
        new_course_two = self._create_course(course_location_two)
        original_key = unicode(course.id)
        new_key_one = unicode(new_course_one.id)
        new_key_two = unicode(new_course_two.id)

        # Run the actual management command
        transfer_students.Command().handle(
            source_course=original_key, dest_course_list=new_key_one + "," + new_key_two
        )

        # Confirm the enrollment mode is verified on the new courses, and enrollment is enabled as appropriate.
        self.assertEquals(('verified', False), CourseEnrollment.enrollment_mode_for_user(student, course.id))
        self.assertEquals(('verified', True), CourseEnrollment.enrollment_mode_for_user(student, new_course_one.id))
        self.assertEquals(('verified', True), CourseEnrollment.enrollment_mode_for_user(student, new_course_two.id))
示例#7
0
    def test_ssl_cms_redirection(self):
        """
        Auto signup auth user and ensure they return to the original
        url they visited after being logged in.
        """
        course = CourseFactory.create(
            org='MITx',
            number='999',
            display_name='Robot Super Course'
        )

        external_auth.views.ssl_login(self._create_ssl_request('/'))
        user = User.objects.get(email=self.USER_EMAIL)
        CourseEnrollment.enroll(user, course.id)

        CourseStaffRole(course.id).add_users(user)
        course_private_url = reverse('course_handler', args=(unicode(course.id),))
        self.assertFalse(SESSION_KEY in self.client.session)

        response = self.client.get(
            course_private_url,
            follow=True,
            SSL_CLIENT_S_DN=self.AUTH_DN.format(self.USER_NAME, self.USER_EMAIL),
            HTTP_ACCEPT='text/html'
        )
        self.assertEqual(('http://testserver{0}'.format(course_private_url), 302),
                         response.redirect_chain[-1])
        self.assertIn(SESSION_KEY, self.client.session)
示例#8
0
    def setUp(self):
        super(TestCertificatesInstructorApiBulkWhiteListExceptions, self).setUp()
        self.global_staff = GlobalStaffFactory()
        self.enrolled_user_1 = UserFactory(
            username='******',
            email='*****@*****.**',
            first_name='Enrolled',
            last_name='Student'
        )
        self.enrolled_user_2 = UserFactory(
            username='******',
            email='*****@*****.**',
            first_name='Enrolled',
            last_name='Student'
        )

        self.not_enrolled_student = UserFactory(
            username='******',
            email='*****@*****.**',
            first_name='NotEnrolled',
            last_name='Student'
        )
        CourseEnrollment.enroll(self.enrolled_user_1, self.course.id)
        CourseEnrollment.enroll(self.enrolled_user_2, self.course.id)

        # Global staff can see the certificates section
        self.client.login(username=self.global_staff.username, password="******")
示例#9
0
    def test_enrolled_students_features_keys_cohorted(self):
        course = CourseFactory.create(org="test", course="course1", display_name="run1")
        course.cohort_config = {'cohorted': True, 'auto_cohort': True, 'auto_cohort_groups': ['cohort']}
        self.store.update_item(course, self.instructor.id)
        cohorted_students = [UserFactory.create() for _ in xrange(10)]
        cohort = CohortFactory.create(name='cohort', course_id=course.id, users=cohorted_students)
        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]')
示例#10
0
    def test_student_progress_with_valid_and_invalid_id(self, default_store):
        """
         Check that invalid 'student_id' raises Http404 for both old mongo and
         split mongo courses.
        """

        # Create new course with respect to 'default_store'
        self.course = CourseFactory.create(default_store=default_store)

        # Invalid Student Ids (Integer and Non-int)
        invalid_student_ids = [
            991021,
            'azU3N_8$',
        ]
        for invalid_id in invalid_student_ids:

            self.assertRaises(
                Http404, views.progress,
                self.request,
                course_id=unicode(self.course.id),
                student_id=invalid_id
            )

        # Enroll student into course
        CourseEnrollment.enroll(self.user, self.course.id, mode='honor')
        resp = views.progress(self.request, course_id=self.course.id.to_deprecated_string(), student_id=self.user.id)
        # Assert that valid 'student_id' returns 200 status
        self.assertEqual(resp.status_code, 200)
示例#11
0
    def setUp(self):
        super(CertificateExceptionViewInstructorApiTest, self).setUp()
        self.global_staff = GlobalStaffFactory()
        self.instructor = InstructorFactory(course_key=self.course.id)
        self.user = UserFactory()
        self.user2 = UserFactory()
        CourseEnrollment.enroll(self.user, self.course.id)
        CourseEnrollment.enroll(self.user2, self.course.id)
        self.url = reverse('certificate_exception_view', kwargs={'course_id': unicode(self.course.id)})

        certificate_white_list_item = CertificateWhitelistFactory.create(
            user=self.user2,
            course_id=self.course.id,
        )

        self.certificate_exception = dict(
            created="",
            notes="Test Notes for Test Certificate Exception",
            user_email='',
            user_id='',
            user_name=unicode(self.user.username)
        )

        self.certificate_exception_in_db = dict(
            id=certificate_white_list_item.id,
            user_name=certificate_white_list_item.user.username,
            notes=certificate_white_list_item.notes,
            user_email=certificate_white_list_item.user.email,
            user_id=certificate_white_list_item.user.id,
        )

        # Enable certificate generation
        cache.clear()
        CertificateGenerationConfiguration.objects.create(enabled=True)
        self.client.login(username=self.global_staff.username, password='******')
示例#12
0
    def purchased_callback(self):
        """
        When purchased, this should enroll the user in the course.  We are assuming that
        course settings for enrollment date are configured such that only if the (user.email, course_id) pair is found
        in CourseEnrollmentAllowed will the user be allowed to enroll.  Otherwise requiring payment
        would in fact be quite silly since there's a clear back door.
        """
        try:
            course_loc = CourseDescriptor.id_to_location(self.course_id)
            course_exists = modulestore().has_item(self.course_id, course_loc)
        except ValueError:
            raise PurchasedCallbackException(
                "The customer purchased Course {0}, but that course doesn't exist!".format(self.course_id))

        if not course_exists:
            raise PurchasedCallbackException(
                "The customer purchased Course {0}, but that course doesn't exist!".format(self.course_id))

        CourseEnrollment.enroll(user=self.user, course_id=self.course_id, mode=self.mode)

        log.info("Enrolled {0} in paid course {1}, paid ${2}".format(self.user.email, self.course_id, self.line_cost))
        org, course_num, run = self.course_id.split("/")
        dog_stats_api.increment(
            "shoppingcart.PaidCourseRegistration.purchased_callback.enrollment",
            tags=["org:{0}".format(org),
                  "course:{0}".format(course_num),
                  "run:{0}".format(run)]
        )
示例#13
0
    def setUpClass(cls):
        super(TestScoreForModule, cls).setUpClass()
        cls.course = CourseFactory.create()
        with cls.store.bulk_operations(cls.course.id):
            cls.a = ItemFactory.create(parent=cls.course, category="chapter", display_name="a")
            cls.b = ItemFactory.create(parent=cls.a, category="sequential", display_name="b")
            cls.c = ItemFactory.create(parent=cls.a, category="sequential", display_name="c")
            cls.d = ItemFactory.create(parent=cls.b, category="vertical", display_name="d")
            cls.e = ItemFactory.create(parent=cls.b, category="vertical", display_name="e")
            cls.f = ItemFactory.create(parent=cls.b, category="vertical", display_name="f")
            cls.g = ItemFactory.create(parent=cls.c, category="vertical", display_name="g")
            cls.h = ItemFactory.create(parent=cls.d, category="problem", display_name="h")
            cls.i = ItemFactory.create(parent=cls.d, category="problem", display_name="i")
            cls.j = ItemFactory.create(parent=cls.e, category="problem", display_name="j")
            cls.k = ItemFactory.create(parent=cls.e, category="html", display_name="k")
            cls.l = ItemFactory.create(parent=cls.e, category="problem", display_name="l")
            cls.m = ItemFactory.create(parent=cls.f, category="html", display_name="m")
            cls.n = ItemFactory.create(parent=cls.g, category="problem", display_name="n")

        cls.request = get_mock_request(UserFactory())
        CourseEnrollment.enroll(cls.request.user, cls.course.id)

        answer_problem(cls.course, cls.request, cls.h, score=2, max_value=5)
        answer_problem(cls.course, cls.request, cls.i, score=3, max_value=5)
        answer_problem(cls.course, cls.request, cls.j, score=0, max_value=1)
        answer_problem(cls.course, cls.request, cls.l, score=1, max_value=3)
        answer_problem(cls.course, cls.request, cls.n, score=3, max_value=10)

        cls.course_grade = CourseGradeFactory().create(cls.request.user, cls.course)
示例#14
0
 def test_no_duplicate_emails_enrolled_staff(self):
     """
     Test that no duplicate emails are sent to a course instructor that is
     also enrolled in the course
     """
     CourseEnrollment.enroll(self.instructor, self.course.id)
     self.test_send_to_all()
    def test_access_student_progress_ccx(self):
        """
        Assert that only a coach can see progress of student.
        """
        ccx_locator = self.make_ccx()
        student = UserFactory()

        # Enroll user
        CourseEnrollment.enroll(student, ccx_locator)

        # Test for access of a coach
        request = self.request_factory.get(reverse('about_course', args=[unicode(ccx_locator)]))
        request.user = self.coach
        mako_middleware_process_request(request)
        resp = views.progress(request, course_id=unicode(ccx_locator), student_id=student.id)
        self.assertEqual(resp.status_code, 200)

        # Assert access of a student
        request = self.request_factory.get(reverse('about_course', args=[unicode(ccx_locator)]))
        request.user = student
        mako_middleware_process_request(request)

        with self.assertRaises(Http404) as context:
            views.progress(request, course_id=unicode(ccx_locator), student_id=self.coach.id)

        self.assertIsNotNone(context.exception)
示例#16
0
    def setUp(self):
        super(TeamAPITestCase, self).setUp()
        self.topics_count = 4
        self.users = {
            "student_unenrolled": UserFactory.create(password=self.test_password),
            "student_enrolled": UserFactory.create(password=self.test_password),
            "student_enrolled_not_on_team": UserFactory.create(password=self.test_password),
            # This student is enrolled in both test courses and is a member of a team in each course, but is not on the
            # same team as student_enrolled.
            "student_enrolled_both_courses_other_team": UserFactory.create(password=self.test_password),
            "staff": AdminFactory.create(password=self.test_password),
            "course_staff": StaffFactory.create(course_key=self.test_course_1.id, password=self.test_password),
        }
        # 'solar team' is intentionally lower case to test case insensitivity in name ordering
        self.test_team_1 = CourseTeamFactory.create(
            name=u"sólar team", course_id=self.test_course_1.id, topic_id="topic_0"
        )
        self.test_team_2 = CourseTeamFactory.create(name="Wind Team", course_id=self.test_course_1.id)
        self.test_team_3 = CourseTeamFactory.create(name="Nuclear Team", course_id=self.test_course_1.id)
        self.test_team_4 = CourseTeamFactory.create(name="Coal Team", course_id=self.test_course_1.id, is_active=False)
        self.test_team_5 = CourseTeamFactory.create(name="Another Team", course_id=self.test_course_2.id)

        for user, course in [
            ("student_enrolled", self.test_course_1),
            ("student_enrolled_not_on_team", self.test_course_1),
            ("student_enrolled_both_courses_other_team", self.test_course_1),
            ("student_enrolled_both_courses_other_team", self.test_course_2),
        ]:
            CourseEnrollment.enroll(self.users[user], course.id, check_access=True)

        self.test_team_1.add_user(self.users["student_enrolled"])
        self.test_team_3.add_user(self.users["student_enrolled_both_courses_other_team"])
        self.test_team_5.add_user(self.users["student_enrolled_both_courses_other_team"])
示例#17
0
 def setUp(self):
     super(GradeTestBase, self).setUp()
     self.request = get_request_for_user(UserFactory())
     self.client.login(username=self.request.user.username, password="******")
     self.subsection_grade_factory = SubsectionGradeFactory(self.request.user)
     self.course_structure = get_course_blocks(self.request.user, self.course.location)
     CourseEnrollment.enroll(self.request.user, self.course.id)
示例#18
0
    def test_refund_cert_callback_before_expiration_email(self):
        """ Test that refund emails are being sent correctly. """
        course = CourseFactory.create()
        course_key = course.id
        many_days = datetime.timedelta(days=60)

        course_mode = CourseMode(course_id=course_key,
                                 mode_slug="verified",
                                 mode_display_name="verified cert",
                                 min_price=self.cost,
                                 expiration_datetime=datetime.datetime.now(pytz.utc) + many_days)
        course_mode.save()

        CourseEnrollment.enroll(self.user, course_key, 'verified')
        cart = Order.get_cart_for_user(user=self.user)
        CertificateItem.add_to_order(cart, course_key, self.cost, 'verified')
        cart.purchase()

        mail.outbox = []
        with patch('shoppingcart.models.log.error') as mock_error_logger:
            CourseEnrollment.unenroll(self.user, course_key)
            self.assertFalse(mock_error_logger.called)
            self.assertEquals(len(mail.outbox), 1)
            self.assertEquals('[Refund] User-Requested Refund', mail.outbox[0].subject)
            self.assertEquals(settings.PAYMENT_SUPPORT_EMAIL, mail.outbox[0].from_email)
            self.assertIn('has requested a refund on Order', mail.outbox[0].body)
示例#19
0
    def test_score_recalculation_on_enrollment_update(self):
        """
        Test that an update in enrollment cause score recalculation.
        Note:
        Score recalculation task must be called with a delay of SCORE_RECALCULATION_DELAY_ON_ENROLLMENT_UPDATE
        """
        course_modes = ['verified', 'audit']

        for mode_slug in course_modes:
            CourseModeFactory.create(
                course_id=self.course.id,
                mode_slug=mode_slug,
                mode_display_name=mode_slug,
            )
        CourseEnrollment.enroll(self.user, self.course.id, mode="audit")

        local_task_args = dict(
            user_id=self.user.id,
            course_key=str(self.course.id)
        )

        with patch(
            'lms.djangoapps.grades.tasks.recalculate_course_and_subsection_grades_for_user.apply_async',
            return_value=None
        ) as mock_task_apply:
            CourseEnrollment.enroll(self.user, self.course.id, mode="verified")
            mock_task_apply.assert_called_once_with(
                countdown=SCORE_RECALCULATION_DELAY_ON_ENROLLMENT_UPDATE,
                kwargs=local_task_args
            )
 def test_enrolled_in_expired(self):
     create_mode(
         self.course, CourseMode.VERIFIED, "Verified Enrollment Track",
         min_price=1, expiration_datetime=datetime.now(pytz.UTC) + timedelta(days=-1)
     )
     CourseEnrollment.enroll(self.student, self.course.id, mode=CourseMode.VERIFIED)
     self.assertEqual("Verified Enrollment Track", self._get_user_group().name)
示例#21
0
    def test_refund_cert_callback_before_expiration(self):
        # If the expiration date has not yet passed on a verified mode, the user can be refunded
        many_days = datetime.timedelta(days=60)

        course = CourseFactory.create()
        self.course_key = course.id
        course_mode = CourseMode(course_id=self.course_key,
                                 mode_slug="verified",
                                 mode_display_name="verified cert",
                                 min_price=self.cost,
                                 expiration_datetime=(datetime.datetime.now(pytz.utc) + many_days))
        course_mode.save()

        # need to prevent analytics errors from appearing in stderr
        with patch('sys.stderr', sys.stdout.write):
            CourseEnrollment.enroll(self.user, self.course_key, 'verified')
            cart = Order.get_cart_for_user(user=self.user)
            CertificateItem.add_to_order(cart, self.course_key, self.cost, 'verified')
            cart.purchase()
            CourseEnrollment.unenroll(self.user, self.course_key)

        target_certs = CertificateItem.objects.filter(course_id=self.course_key, user_id=self.user, status='refunded', mode='verified')
        self.assertTrue(target_certs[0])
        self.assertTrue(target_certs[0].refund_requested_time)
        self.assertEquals(target_certs[0].order.status, 'refunded')
        self._assert_refund_tracked()
示例#22
0
    def test_program_courses_with_invalid_data(self, key_remove, log_warn):
        """Test programs with invalid responses."""

        CourseEnrollment.enroll(self.user, self.course_1.id)
        self.client.login(username="******", password="******")
        self.create_config()

        program_data = self._create_program_data([(self.course_1.id, 'active')])
        if key_remove and key_remove in program_data[unicode(self.course_1.id)]:
            del program_data[unicode(self.course_1.id)][key_remove]

        with patch('student.views.get_programs_for_dashboard') as mock_data:
            mock_data.return_value = program_data

            response = self.client.get(reverse('dashboard'))

            # if data is invalid then warning log will be recorded.
            if key_remove:
                log_warn.assert_called_with(
                    'Program structure is invalid, skipping display: %r', program_data[
                        unicode(self.course_1.id)
                    ]
                )
                # verify that no programs related upsell messages appear on the
                # student dashboard.
                self._assert_responses(response, 0)
            else:
                # in case of valid data all upsell messages will appear on dashboard.
                self._assert_responses(response, 1)

            # verify that only normal courses (non-programs courses) appear on
            # the student dashboard.
            self.assertContains(response, 'course-container', 1)
            self.assertIn('Pursue a Certificate of Achievement to highlight', response.content)
示例#23
0
def enroll_user(user, course_key):
    # Activate user
    registration = world.RegistrationFactory(user=user)
    registration.register(user)
    registration.activate()
    # Enroll them in the course
    CourseEnrollment.enroll(user, course_key)
示例#24
0
 def test_retirement(self):
     """
     Tests that calling the retirement method for a specific enrollment retires
     the enrolled_email and reason columns of each row associated with that
     enrollment.
     """
     enrollment = CourseEnrollment.enroll(self.user, self.course.id)
     other_enrollment = CourseEnrollment.enroll(self.user, self.other_course.id)
     ManualEnrollmentAudit.create_manual_enrollment_audit(
         self.instructor, self.user.email, ALLOWEDTOENROLL_TO_ENROLLED,
         'manually enrolling unenrolled user', enrollment
     )
     ManualEnrollmentAudit.create_manual_enrollment_audit(
         self.instructor, self.user.email, ALLOWEDTOENROLL_TO_ENROLLED,
         'manually enrolling unenrolled user again', enrollment
     )
     ManualEnrollmentAudit.create_manual_enrollment_audit(
         self.instructor, self.user.email, ALLOWEDTOENROLL_TO_ENROLLED,
         'manually enrolling unenrolled user', other_enrollment
     )
     ManualEnrollmentAudit.create_manual_enrollment_audit(
         self.instructor, self.user.email, ALLOWEDTOENROLL_TO_ENROLLED,
         'manually enrolling unenrolled user again', other_enrollment
     )
     self.assertTrue(ManualEnrollmentAudit.objects.filter(enrollment=enrollment).exists())
     # retire the ManualEnrollmentAudit objects associated with the above enrollments
     enrollments = CourseEnrollment.objects.filter(user=self.user)
     ManualEnrollmentAudit.retire_manual_enrollments(enrollments=enrollments, retired_email="xxx")
     self.assertTrue(ManualEnrollmentAudit.objects.filter(enrollment=enrollment).exists())
     self.assertFalse(ManualEnrollmentAudit.objects.filter(enrollment=enrollment).exclude(
         enrolled_email="xxx"
     ))
     self.assertFalse(ManualEnrollmentAudit.objects.filter(enrollment=enrollment).exclude(
         reason=""
     ))
示例#25
0
 def setUp(self):
     super(TestVariedMetadata, self).setUp()
     self.course = CourseFactory.create()
     with self.store.bulk_operations(self.course.id):
         self.chapter = ItemFactory.create(
             parent=self.course,
             category="chapter",
             display_name="Test Chapter"
         )
         self.sequence = ItemFactory.create(
             parent=self.chapter,
             category='sequential',
             display_name="Test Sequential 1",
             graded=True
         )
         self.vertical = ItemFactory.create(
             parent=self.sequence,
             category='vertical',
             display_name='Test Vertical 1'
         )
     self.problem_xml = u'''
         <problem url_name="capa-optionresponse">
           <optionresponse>
             <optioninput options="('Correct', 'Incorrect')" correct="Correct"></optioninput>
             <optioninput options="('Correct', 'Incorrect')" correct="Correct"></optioninput>
           </optionresponse>
         </problem>
     '''
     self.addCleanup(set_current_request, None)
     self.request = get_mock_request(UserFactory())
     self.client.login(username=self.request.user.username, password="******")
     CourseEnrollment.enroll(self.request.user, self.course.id)
示例#26
0
    def test_staff_level(self):
        """
        Ensure that a staff member can't access instructor endpoints.
        """
        staff_member = StaffFactory(self.course)
        CourseEnrollment.enroll(staff_member, self.course.id)
        self.client.login(username=staff_member.username, password='******')
        # Try to promote to forums admin - not working
        # update_forum_role(self.course.id, staff_member, FORUM_ROLE_ADMINISTRATOR, 'allow')

        for endpoint, args in self.staff_level_endpoints:
            # TODO: make these work
            if endpoint in ['update_forum_role_membership', 'proxy_legacy_analytics', 'list_forum_members']:
                continue
            self._access_endpoint(
                endpoint,
                args,
                200,
                "Staff member should be allowed to access endpoint " + endpoint
            )

        for endpoint, args in self.instructor_level_endpoints:
            self._access_endpoint(
                endpoint,
                args,
                403,
                "Staff member should not be allowed to access endpoint " + endpoint
            )
示例#27
0
    def test_instructor_level(self):
        """
        Ensure that an instructor member can access all endpoints.
        """
        inst = InstructorFactory(self.course)
        CourseEnrollment.enroll(inst, self.course.id)
        self.client.login(username=inst.username, password='******')

        for endpoint, args in self.staff_level_endpoints:
            # TODO: make these work
            if endpoint in ['update_forum_role_membership', 'proxy_legacy_analytics']:
                continue
            self._access_endpoint(
                endpoint,
                args,
                200,
                "Instructor should be allowed to access endpoint " + endpoint
            )

        for endpoint, args in self.instructor_level_endpoints:
            # TODO: make this work
            if endpoint in ['rescore_problem']:
                continue
            self._access_endpoint(
                endpoint,
                args,
                200,
                "Instructor should be allowed to access endpoint " + endpoint
            )
示例#28
0
    def test_modes_program_courses_on_dashboard_with_configuration(self, course_mode):
        """Test that if program configuration is enabled than student can only
        see those courses with xseries upsell messages which are active in
        xseries programs.
        """
        CourseEnrollment.enroll(self.user, self.course_1.id, mode=course_mode)
        CourseEnrollment.enroll(self.user, self.course_2.id, mode=course_mode)

        self.client.login(username="******", password="******")
        self.create_config()

        with patch('student.views.get_programs_for_dashboard') as mock_data:
            mock_data.return_value = self._create_program_data(
                [(self.course_1.id, 'active'), (self.course_2.id, 'unpublished')]
            )
            response = self.client.get(reverse('dashboard'))
            # count total courses appearing on student dashboard
            self.assertContains(response, 'course-container', 2)
            self._assert_responses(response, 1)

            # for verified enrollment view the program detail button will have
            # the class 'base-btn'
            # for other modes view the program detail button will have have the
            # class border-btn
            if course_mode == 'verified':
                self.assertIn('xseries-base-btn', response.content)
            else:
                self.assertIn('xseries-border-btn', response.content)
示例#29
0
    def test_activation(self):
        user = User.objects.create(username="******", email="*****@*****.**")
        course_id = "edX/Test101/2013"
        self.assertFalse(CourseEnrollment.is_enrolled(user, course_id))

        # Creating an enrollment doesn't actually enroll a student
        # (calling CourseEnrollment.enroll() would have)
        enrollment = CourseEnrollment.create_enrollment(user, course_id)
        self.assertFalse(CourseEnrollment.is_enrolled(user, course_id))

        # Until you explicitly activate it
        enrollment.activate()
        self.assertTrue(CourseEnrollment.is_enrolled(user, course_id))

        # Activating something that's already active does nothing
        enrollment.activate()
        self.assertTrue(CourseEnrollment.is_enrolled(user, course_id))

        # Now deactive
        enrollment.deactivate()
        self.assertFalse(CourseEnrollment.is_enrolled(user, course_id))

        # Deactivating something that's already inactive does nothing
        enrollment.deactivate()
        self.assertFalse(CourseEnrollment.is_enrolled(user, course_id))

        # A deactivated enrollment should be activated if enroll() is called
        # for that user/course_id combination
        CourseEnrollment.enroll(user, course_id)
        self.assertTrue(CourseEnrollment.is_enrolled(user, course_id))
    def setUp(self):
        super(LmsSearchFilterGeneratorTestCase, self).setUp()
        self.build_courses()
        self.user = UserFactory.create(username="******", email="*****@*****.**", password='******')

        for course in self.courses:
            CourseEnrollment.enroll(self.user, course.location.course_key)
示例#31
0
 def setUp(self):
     super(TestCourseGradeLogging, self).setUp()
     self.course = CourseFactory.create()
     with self.store.bulk_operations(self.course.id):
         self.chapter = ItemFactory.create(
             parent=self.course,
             category="chapter",
             display_name="Test Chapter"
         )
         self.sequence = ItemFactory.create(
             parent=self.chapter,
             category='sequential',
             display_name="Test Sequential 1",
             graded=True
         )
         self.sequence_2 = ItemFactory.create(
             parent=self.chapter,
             category='sequential',
             display_name="Test Sequential 2",
             graded=True
         )
         self.sequence_3 = ItemFactory.create(
             parent=self.chapter,
             category='sequential',
             display_name="Test Sequential 3",
             graded=False
         )
         self.vertical = ItemFactory.create(
             parent=self.sequence,
             category='vertical',
             display_name='Test Vertical 1'
         )
         self.vertical_2 = ItemFactory.create(
             parent=self.sequence_2,
             category='vertical',
             display_name='Test Vertical 2'
         )
         self.vertical_3 = ItemFactory.create(
             parent=self.sequence_3,
             category='vertical',
             display_name='Test Vertical 3'
         )
         problem_xml = MultipleChoiceResponseXMLFactory().build_xml(
             question_text='The correct answer is Choice 2',
             choices=[False, False, True, False],
             choice_names=['choice_0', 'choice_1', 'choice_2', 'choice_3']
         )
         self.problem = ItemFactory.create(
             parent=self.vertical,
             category="problem",
             display_name="test_problem_1",
             data=problem_xml
         )
         self.problem_2 = ItemFactory.create(
             parent=self.vertical_2,
             category="problem",
             display_name="test_problem_2",
             data=problem_xml
         )
         self.problem_3 = ItemFactory.create(
             parent=self.vertical_3,
             category="problem",
             display_name="test_problem_3",
             data=problem_xml
         )
     self.request = get_mock_request(UserFactory())
     self.client.login(username=self.request.user.username, password="******")
     self.course_structure = get_course_blocks(self.request.user, self.course.location)
     self.subsection_grade_factory = SubsectionGradeFactory(self.request.user, self.course, self.course_structure)
     CourseEnrollment.enroll(self.request.user, self.course.id)
 def test_no_upgrade_message_if_flag_disabled(self):
     self.flag.everyone = False
     self.flag.save()
     CourseEnrollment.enroll(self.user, self.course.id, CourseMode.AUDIT)
     self.assert_upgrade_message_not_displayed()
 def test_no_upgrade_message_if_verified_track(self):
     CourseEnrollment.enroll(self.user, self.course.id, CourseMode.VERIFIED)
     self.assert_upgrade_message_not_displayed()
    def test_course_messaging(self):
        """
        Ensure that the following four use cases work as expected

        1) Anonymous users are shown a course message linking them to the login page
        2) Unenrolled users are shown a course message allowing them to enroll
        3) Enrolled users who show up on the course page after the course has begun
        are not shown a course message.
        4) Enrolled users who show up on the course page after the course has begun will
        see the course expiration banner if course duration limits are on for the course.
        5) Enrolled users who show up on the course page before the course begins
        are shown a message explaining when the course starts as well as a call to
        action button that allows them to add a calendar event.
        """
        # Verify that anonymous users are shown a login link in the course message
        url = course_home_url(self.course)
        response = self.client.get(url)
        self.assertContains(response, TEST_COURSE_HOME_MESSAGE)
        self.assertContains(response, TEST_COURSE_HOME_MESSAGE_ANONYMOUS)

        # Verify that unenrolled users are shown an enroll call to action message
        user = self.create_user_for_course(self.course,
                                           CourseUserType.UNENROLLED)
        url = course_home_url(self.course)
        response = self.client.get(url)
        self.assertContains(response, TEST_COURSE_HOME_MESSAGE)
        self.assertContains(response, TEST_COURSE_HOME_MESSAGE_UNENROLLED)

        # Verify that enrolled users are not shown any state warning message when enrolled and course has begun.
        CourseEnrollment.enroll(user, self.course.id)
        url = course_home_url(self.course)
        response = self.client.get(url)
        self.assertNotContains(response, TEST_COURSE_HOME_MESSAGE_ANONYMOUS)
        self.assertNotContains(response, TEST_COURSE_HOME_MESSAGE_UNENROLLED)
        self.assertNotContains(response, TEST_COURSE_HOME_MESSAGE_PRE_START)

        # Verify that enrolled users are shown the course expiration banner if content gating is enabled

        # We use .save() explicitly here (rather than .objects.create) in order to force the
        # cache to refresh.
        config = CourseDurationLimitConfig(course=CourseOverview.get_from_id(
            self.course.id),
                                           enabled=True,
                                           enabled_as_of=datetime(2018, 1, 1))
        config.save()

        url = course_home_url(self.course)
        response = self.client.get(url)
        bannerText = get_expiration_banner_text(user, self.course)
        self.assertContains(response, bannerText, html=True)

        # Verify that enrolled users are not shown the course expiration banner if content gating is disabled
        config.enabled = False
        config.save()
        url = course_home_url(self.course)
        response = self.client.get(url)
        bannerText = get_expiration_banner_text(user, self.course)
        self.assertNotContains(response, bannerText, html=True)

        # Verify that enrolled users are shown 'days until start' message before start date
        future_course = self.create_future_course()
        CourseEnrollment.enroll(user, future_course.id)
        url = course_home_url(future_course)
        response = self.client.get(url)
        self.assertContains(response, TEST_COURSE_HOME_MESSAGE)
        self.assertContains(response, TEST_COURSE_HOME_MESSAGE_PRE_START)
 def test_display_upgrade_message_if_audit_and_deadline_not_passed(self):
     CourseEnrollment.enroll(self.user, self.course.id, CourseMode.AUDIT)
     self.assert_upgrade_message_displayed()
 def setUpTestData(cls):
     """Set up and enroll our fake user in the course."""
     cls.staff_user = StaffFactory(course_key=cls.course.id,
                                   password=TEST_PASSWORD)
     cls.user = UserFactory(password=TEST_PASSWORD)
     CourseEnrollment.enroll(cls.user, cls.course.id)
示例#37
0
文件: testutils.py 项目: saadow123/1
 def enroll(self, course_id=None):
     """Enroll test user in test course."""
     CourseEnrollment.enroll(self.user, course_id or self.course.id)
    def setUp(self):
        """
        Test case scaffolding
        """
        super(EntranceExamTestCases, self).setUp()
        self.course = CourseFactory.create(
            metadata={
                'entrance_exam_enabled': True,
            }
        )
        with self.store.bulk_operations(self.course.id):
            self.chapter = ItemFactory.create(
                parent=self.course,
                display_name='Overview'
            )
            self.welcome = ItemFactory.create(
                parent=self.chapter,
                display_name='Welcome'
            )
            ItemFactory.create(
                parent=self.course,
                category='chapter',
                display_name="Week 1"
            )
            self.chapter_subsection = ItemFactory.create(
                parent=self.chapter,
                category='sequential',
                display_name="Lesson 1"
            )
            chapter_vertical = ItemFactory.create(
                parent=self.chapter_subsection,
                category='vertical',
                display_name='Lesson 1 Vertical - Unit 1'
            )
            ItemFactory.create(
                parent=chapter_vertical,
                category="problem",
                display_name="Problem - Unit 1 Problem 1"
            )
            ItemFactory.create(
                parent=chapter_vertical,
                category="problem",
                display_name="Problem - Unit 1 Problem 2"
            )

            ItemFactory.create(
                category="instructor",
                parent=self.course,
                data="Instructor Tab",
                display_name="Instructor"
            )
            self.entrance_exam = ItemFactory.create(
                parent=self.course,
                category="chapter",
                display_name="Entrance Exam Section - Chapter 1",
                is_entrance_exam=True,
                in_entrance_exam=True
            )
            self.exam_1 = ItemFactory.create(
                parent=self.entrance_exam,
                category='sequential',
                display_name="Exam Sequential - Subsection 1",
                graded=True,
                in_entrance_exam=True
            )
            subsection = ItemFactory.create(
                parent=self.exam_1,
                category='vertical',
                display_name='Exam Vertical - Unit 1'
            )
            problem_xml = MultipleChoiceResponseXMLFactory().build_xml(
                question_text='The correct answer is Choice 3',
                choices=[False, False, True, False],
                choice_names=['choice_0', 'choice_1', 'choice_2', 'choice_3']
            )
            self.problem_1 = ItemFactory.create(
                parent=subsection,
                category="problem",
                display_name="Exam Problem - Problem 1",
                data=problem_xml
            )
            self.problem_2 = ItemFactory.create(
                parent=subsection,
                category="problem",
                display_name="Exam Problem - Problem 2"
            )

        add_entrance_exam_milestone(self.course, self.entrance_exam)

        self.course.entrance_exam_enabled = True
        self.course.entrance_exam_minimum_score_pct = 0.50
        self.course.entrance_exam_id = unicode(self.entrance_exam.scope_ids.usage_id)

        self.anonymous_user = AnonymousUserFactory()
        self.addCleanup(set_current_request, None)
        self.request = get_mock_request(UserFactory())
        modulestore().update_item(self.course, self.request.user.id)

        self.client.login(username=self.request.user.username, password="******")
        CourseEnrollment.enroll(self.request.user, self.course.id)

        self.expected_locked_toc = (
            [
                {
                    'active': True,
                    'sections': [
                        {
                            'url_name': u'Exam_Sequential_-_Subsection_1',
                            'display_name': u'Exam Sequential - Subsection 1',
                            'graded': True,
                            'format': '',
                            'due': None,
                            'active': True
                        }
                    ],
                    'url_name': u'Entrance_Exam_Section_-_Chapter_1',
                    'display_name': u'Entrance Exam Section - Chapter 1',
                    'display_id': u'entrance-exam-section-chapter-1',
                }
            ]
        )
        self.expected_unlocked_toc = (
            [
                {
                    'active': False,
                    'sections': [
                        {
                            'url_name': u'Welcome',
                            'display_name': u'Welcome',
                            'graded': False,
                            'format': '',
                            'due': None,
                            'active': False
                        },
                        {
                            'url_name': u'Lesson_1',
                            'display_name': u'Lesson 1',
                            'graded': False,
                            'format': '',
                            'due': None,
                            'active': False
                        }
                    ],
                    'url_name': u'Overview',
                    'display_name': u'Overview',
                    'display_id': u'overview'
                },
                {
                    'active': False,
                    'sections': [],
                    'url_name': u'Week_1',
                    'display_name': u'Week 1',
                    'display_id': u'week-1'
                },
                {
                    'active': False,
                    'sections': [],
                    'url_name': u'Instructor',
                    'display_name': u'Instructor',
                    'display_id': u'instructor'
                },
                {
                    'active': True,
                    'sections': [
                        {
                            'url_name': u'Exam_Sequential_-_Subsection_1',
                            'display_name': u'Exam Sequential - Subsection 1',
                            'graded': True,
                            'format': '',
                            'due': None,
                            'active': True
                        }
                    ],
                    'url_name': u'Entrance_Exam_Section_-_Chapter_1',
                    'display_name': u'Entrance Exam Section - Chapter 1',
                    'display_id': u'entrance-exam-section-chapter-1'
                }
            ]
        )
示例#39
0
def create_new_course(request):
    """
    Create a new course.

    Returns the URL for the course overview page.
    """
    if not auth.has_access(request.user, CourseCreatorRole()):
        raise PermissionDenied()

    org = request.json.get('org')
    number = request.json.get('number')
    display_name = request.json.get('display_name')
    run = request.json.get('run')

    try:
        dest_location = Location(u'i4x', org, number, u'course', run)
    except InvalidLocationError as error:
        return JsonResponse({
            "ErrMsg":
            _("Unable to create course '{name}'.\n\n{err}").format(
                name=display_name, err=error.message)
        })

    # see if the course already exists
    existing_course = None
    try:
        existing_course = modulestore('direct').get_item(dest_location)
    except ItemNotFoundError:
        pass
    if existing_course is not None:
        return JsonResponse({
            'ErrMsg':
            _('There is already a course defined with the same '
              'organization, course number, and course run. Please '
              'change either organization or course number to be '
              'unique.'),
            'OrgErrMsg':
            _('Please change either the organization or '
              'course number so that it is unique.'),
            'CourseErrMsg':
            _('Please change either the organization or '
              'course number so that it is unique.'),
        })

    # dhm: this query breaks the abstraction, but I'll fix it when I do my suspended refactoring of this
    # file for new locators. get_items should accept a query rather than requiring it be a legal location
    course_search_location = bson.son.SON({
        '_id.tag':
        'i4x',
        # cannot pass regex to Location constructor; thus this hack
        # pylint: disable=E1101
        '_id.org':
        re.compile(u'^{}$'.format(dest_location.org),
                   re.IGNORECASE | re.UNICODE),
        # pylint: disable=E1101
        '_id.course':
        re.compile(u'^{}$'.format(dest_location.course),
                   re.IGNORECASE | re.UNICODE),
        '_id.category':
        'course',
    })
    courses = modulestore().collection.find(course_search_location,
                                            fields=('_id'))
    if courses.count() > 0:
        return JsonResponse({
            'ErrMsg':
            _('There is already a course defined with the same '
              'organization and course number. Please '
              'change at least one field to be unique.'),
            'OrgErrMsg':
            _('Please change either the organization or '
              'course number so that it is unique.'),
            'CourseErrMsg':
            _('Please change either the organization or '
              'course number so that it is unique.'),
        })

    # instantiate the CourseDescriptor and then persist it
    # note: no system to pass
    if display_name is None:
        metadata = {}
    else:
        metadata = {'display_name': display_name}
    modulestore('direct').create_and_save_xmodule(dest_location,
                                                  metadata=metadata)
    new_course = modulestore('direct').get_item(dest_location)

    # clone a default 'about' overview module as well
    dest_about_location = dest_location.replace(category='about',
                                                name='overview')
    overview_template = AboutDescriptor.get_template('overview.yaml')
    modulestore('direct').create_and_save_xmodule(
        dest_about_location,
        system=new_course.system,
        definition_data=overview_template.get('data'))

    initialize_course_tabs(new_course)

    new_location = loc_mapper().translate_location(
        new_course.location.course_id, new_course.location, False, True)
    # can't use auth.add_users here b/c it requires request.user to already have Instructor perms in this course
    # however, we can assume that b/c this user had authority to create the course, the user can add themselves
    CourseInstructorRole(new_location).add_users(request.user)
    auth.add_users(request.user, CourseStaffRole(new_location), request.user)

    # seed the forums
    seed_permissions_roles(new_course.location.course_id)

    # auto-enroll the course creator in the course so that "View Live" will
    # work.
    CourseEnrollment.enroll(request.user, new_course.location.course_id)

    return JsonResponse({'url': new_location.url_reverse("course/", "")})
示例#40
0
    def post(self, request, course_id):
        """Takes the form submission from the page and parses it.

        Args:
            request (`Request`): The Django Request object.
            course_id (unicode): The slash-separated course key.

        Returns:
            Status code 400 when the requested mode is unsupported. When the honor mode
            is selected, redirects to the dashboard. When the verified mode is selected,
            returns error messages if the indicated contribution amount is invalid or
            below the minimum, otherwise redirects to the verification flow.

        """
        course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
        user = request.user

        # This is a bit redundant with logic in student.views.change_enrollment,
        # but I don't really have the time to refactor it more nicely and test.
        course = modulestore().get_course(course_key)
        if not has_access(user, 'enroll', course):
            error_msg = _("Enrollment is closed")
            return self.get(request, course_id, error=error_msg)

        requested_mode = self._get_requested_mode(request.POST)

        allowed_modes = CourseMode.modes_for_course_dict(course_key)
        if requested_mode not in allowed_modes:
            return HttpResponseBadRequest(_("Enrollment mode not supported"))

        if requested_mode == 'audit':
            # The user will have already been enrolled in the audit mode at this
            # point, so we just redirect them to the dashboard, thereby avoiding
            # hitting the database a second time attempting to enroll them.
            return redirect(reverse('dashboard'))

        if requested_mode == 'honor':
            CourseEnrollment.enroll(user, course_key, mode=requested_mode)
            return redirect(get_course_success_page_url(course_id))

        mode_info = allowed_modes[requested_mode]

        if requested_mode == 'verified':
            amount = request.POST.get("contribution") or \
                request.POST.get("contribution-other-amt") or 0

            try:
                # Validate the amount passed in and force it into two digits
                amount_value = decimal.Decimal(amount).quantize(
                    decimal.Decimal('.01'), rounding=decimal.ROUND_DOWN)
            except decimal.InvalidOperation:
                error_msg = _("Invalid amount selected.")
                return self.get(request, course_id, error=error_msg)

            # Check for minimum pricing
            if amount_value < mode_info.min_price:
                error_msg = _(
                    "No selected price or selected price is too low.")
                return self.get(request, course_id, error=error_msg)

            donation_for_course = request.session.get("donation_for_course",
                                                      {})
            donation_for_course[unicode(course_key)] = amount_value
            request.session["donation_for_course"] = donation_for_course

            return redirect(
                reverse('verify_student_start_flow',
                        kwargs={'course_id': unicode(course_key)}))
    def test_look_up_valid_registration_code(self):
        """
        test lookup for the valid registration code
        and that registration code has been redeemed by user
        and then mark the registration code as in_valid
        when marking as invalidate, it also lookup for
        registration redemption entry and also delete
        that redemption entry and un_enroll the student
        who used that registration code for their enrollment.
        """
        for i in range(2):
            CourseRegistrationCode.objects.create(
                code='reg_code{}'.format(i),
                course_id=six.text_type(self.course.id),
                created_by=self.instructor,
                invoice=self.sale_invoice,
                invoice_item=self.invoice_item,
                mode_slug=CourseMode.DEFAULT_MODE_SLUG
            )

        reg_code = CourseRegistrationCode.objects.all()[0]
        student = UserFactory()
        enrollment = CourseEnrollment.enroll(student, self.course.id)

        RegistrationCodeRedemption.objects.create(
            registration_code=reg_code,
            redeemed_by=student,
            course_enrollment=enrollment
        )

        data = {
            'registration_code': reg_code.code
        }
        response = self.client.get(self.lookup_code_url, data)
        self.assertEqual(response.status_code, 200)
        json_dict = json.loads(response.content)
        self.assertTrue(json_dict['is_registration_code_valid'])
        self.assertTrue(json_dict['is_registration_code_redeemed'])

        # now mark that registration code as invalid
        data = {
            'registration_code': reg_code.code,
            'action_type': 'invalidate_registration_code'
        }
        response = self.client.post(self.registration_code_detail_url, data)
        self.assertEqual(response.status_code, 200)

        json_dict = json.loads(response.content)
        message = _('This enrollment code has been canceled. It can no longer be used.')
        self.assertEqual(message, json_dict['message'])

        # now check that the registration code should be marked as invalid in the db.
        reg_code = CourseRegistrationCode.objects.get(code=reg_code.code)
        self.assertEqual(reg_code.is_valid, False)

        redemption = RegistrationCodeRedemption.get_registration_code_redemption(reg_code.code, self.course.id)
        self.assertIsNone(redemption)

        # now the student course enrollment should be false.
        enrollment = CourseEnrollment.get_enrollment(student, self.course.id)
        self.assertEqual(enrollment.is_active, False)
示例#42
0
 def setUp(self):
     self.user = UserFactory.create()
     self.course = CourseFactory.create()
     CourseEnrollment.enroll(self.user, self.course.id)
     self.client.login(username=self.user.username, password='******')
示例#43
0
 def setUp(self):
     super(ComputeGradesForCourseTest, self).setUp()
     self.users = [UserFactory.create() for _ in range(12)]
     self.set_up_course()
     for user in self.users:
         CourseEnrollment.enroll(user, self.course.id)
示例#44
0
 def setUpTestData(cls):
     """Set up and enroll our fake user in the course."""
     cls.user = UserFactory(password=TEST_PASSWORD)
     for course in cls.courses:
         CourseEnrollment.enroll(cls.user, course.id)
示例#45
0
 def test_offer_html(self):
     CourseEnrollment.enroll(self.user, self.course.id)
     self.assertEqual(self.client.get(self.url).data['offer_html'], '<p>Offer</p>')
示例#46
0
 def setUp(self):
     super(RecalculateGradesForUserTest, self).setUp()
     self.user = UserFactory.create()
     self.set_up_course()
     CourseEnrollment.enroll(self.user, self.course.id)
示例#47
0
 def test_handouts(self):
     CourseEnrollment.enroll(self.user, self.course.id)
     self.store.create_item(self.user.id, self.course.id, 'course_info', 'handouts', fields={'data': '<p>Hi</p>'})
     self.assertEqual(self.client.get(self.url).data['handouts_html'], '<p>Hi</p>')
示例#48
0
 def test_course_expired_html(self):
     CourseEnrollment.enroll(self.user, self.course.id)
     self.assertEqual(self.client.get(self.url).data['course_expired_html'], '<p>Expired</p>')
示例#49
0
    def test_cea_enrolls_only_one_user(self):
        """
        Tests that a CourseEnrollmentAllowed can be used by just one user.
        If the user changes e-mail and then a second user tries to enroll with the same accepted e-mail,
        the second enrollment should fail.
        However, the original user can reuse the CEA many times.
        """

        cea = CourseEnrollmentAllowedFactory(
            email='*****@*****.**',
            course_id=self.course.id,
            auto_enroll=False,
        )
        # Still unlinked
        self.assertIsNone(cea.user)

        user1 = UserFactory.create(username="******",
                                   email="*****@*****.**",
                                   password="******")
        user2 = UserFactory.create(username="******",
                                   email="*****@*****.**",
                                   password="******")

        self.assertFalse(
            CourseEnrollment.objects.filter(course_id=self.course.id,
                                            user=user1).exists())

        user1.email = '*****@*****.**'
        user1.save()

        CourseEnrollment.enroll(user1, self.course.id, check_access=True)

        self.assertTrue(
            CourseEnrollment.objects.filter(course_id=self.course.id,
                                            user=user1).exists())

        # The CEA is now linked
        cea.refresh_from_db()
        self.assertEqual(cea.user, user1)

        # user2 wants to enroll too, (ab)using the same allowed e-mail, but cannot
        user1.email = '*****@*****.**'
        user1.save()
        user2.email = '*****@*****.**'
        user2.save()
        with self.assertRaises(EnrollmentClosedError):
            CourseEnrollment.enroll(user2, self.course.id, check_access=True)

        # CEA still linked to user1. Also after unenrolling
        cea.refresh_from_db()
        self.assertEqual(cea.user, user1)

        CourseEnrollment.unenroll(user1, self.course.id)

        cea.refresh_from_db()
        self.assertEqual(cea.user, user1)

        # Enroll user1 again. Because it's the original owner of the CEA, the enrollment is allowed
        CourseEnrollment.enroll(user1, self.course.id, check_access=True)

        # Still same
        cea.refresh_from_db()
        self.assertEqual(cea.user, user1)
示例#50
0
 def test_waffle_flag_disabled(self, enrollment_mode):
     CourseEnrollment.enroll(self.user, self.course.id, enrollment_mode)
     response = self.client.get(self.url)
     self.assertEqual(response.status_code, 404)
示例#51
0
 def test_refund_cert_no_cert_exists(self):
     # If there is no paid certificate, the refund callback should return nothing
     CourseEnrollment.enroll(self.user, self.course_key, 'verified')
     ret_val = CourseEnrollment.unenroll(self.user, self.course_key)
     self.assertFalse(ret_val)
示例#52
0
    def test_transfer_students(self):
        """ Verify the transfer student command works as intended. """
        student = UserFactory.create()
        student.set_password(self.PASSWORD)
        student.save()
        mode = 'verified'
        # Original Course
        original_course_location = locator.CourseLocator('Org0', 'Course0', 'Run0')
        course = self._create_course(original_course_location)
        # Enroll the student in 'verified'
        CourseEnrollment.enroll(student, course.id, mode="verified")

        # Create and purchase a verified cert for the original course.
        self._create_and_purchase_verified(student, course.id)

        # New Course 1
        course_location_one = locator.CourseLocator('Org1', 'Course1', 'Run1')
        new_course_one = self._create_course(course_location_one)

        # New Course 2
        course_location_two = locator.CourseLocator('Org2', 'Course2', 'Run2')
        new_course_two = self._create_course(course_location_two)
        original_key = unicode(course.id)
        new_key_one = unicode(new_course_one.id)
        new_key_two = unicode(new_course_two.id)

        # Run the actual management command
        transfer_students.Command().handle(
            source_course=original_key, dest_course_list=new_key_one + "," + new_key_two
        )
        self.assertTrue(self.signal_fired)

        # Confirm the analytics event was emitted.
        self.mock_tracker.emit.assert_has_calls(  # pylint: disable=maybe-no-member
            [
                call(
                    EVENT_NAME_ENROLLMENT_ACTIVATED,
                    {'course_id': original_key, 'user_id': student.id, 'mode': mode}
                ),
                call(
                    EVENT_NAME_ENROLLMENT_MODE_CHANGED,
                    {'course_id': original_key, 'user_id': student.id, 'mode': mode}
                ),
                call(
                    EVENT_NAME_ENROLLMENT_DEACTIVATED,
                    {'course_id': original_key, 'user_id': student.id, 'mode': mode}
                ),
                call(
                    EVENT_NAME_ENROLLMENT_ACTIVATED,
                    {'course_id': new_key_one, 'user_id': student.id, 'mode': mode}
                ),
                call(
                    EVENT_NAME_ENROLLMENT_MODE_CHANGED,
                    {'course_id': new_key_one, 'user_id': student.id, 'mode': mode}
                ),
                call(
                    EVENT_NAME_ENROLLMENT_ACTIVATED,
                    {'course_id': new_key_two, 'user_id': student.id, 'mode': mode}
                ),
                call(
                    EVENT_NAME_ENROLLMENT_MODE_CHANGED,
                    {'course_id': new_key_two, 'user_id': student.id, 'mode': mode}
                )
            ]
        )
        self.mock_tracker.reset_mock()

        # Confirm the enrollment mode is verified on the new courses, and enrollment is enabled as appropriate.
        self.assertEquals((mode, False), CourseEnrollment.enrollment_mode_for_user(student, course.id))
        self.assertEquals((mode, True), CourseEnrollment.enrollment_mode_for_user(student, new_course_one.id))
        self.assertEquals((mode, True), CourseEnrollment.enrollment_mode_for_user(student, new_course_two.id))

        # Confirm the student has not be refunded.
        target_certs = CertificateItem.objects.filter(
            course_id=course.id, user_id=student, status='purchased', mode=mode
        )
        self.assertTrue(target_certs[0])
        self.assertFalse(target_certs[0].refund_requested_time)
        self.assertEquals(target_certs[0].order.status, 'purchased')
示例#53
0
    def setUp(self):
        super(TeamAPITestCase, self).setUp()
        self.topics_count = 4
        self.users = {
            'staff':
            AdminFactory.create(password=self.test_password),
            'course_staff':
            StaffFactory.create(course_key=self.test_course_1.id,
                                password=self.test_password)
        }
        self.create_and_enroll_student(username='******')
        self.create_and_enroll_student(username='******')
        self.create_and_enroll_student(username='******',
                                       courses=[])

        # Make this student a community TA.
        self.create_and_enroll_student(username='******')
        seed_permissions_roles(self.test_course_1.id)
        community_ta_role = Role.objects.get(name=FORUM_ROLE_COMMUNITY_TA,
                                             course_id=self.test_course_1.id)
        community_ta_role.users.add(self.users['community_ta'])

        # This student is enrolled in both test courses and is a member of a team in each course, but is not on the
        # same team as student_enrolled.
        self.create_and_enroll_student(
            courses=[self.test_course_1, self.test_course_2],
            username='******')

        # Make this student have a public profile
        self.create_and_enroll_student(
            courses=[self.test_course_2],
            username='******')
        profile = self.users['student_enrolled_public_profile'].profile
        profile.year_of_birth = 1970
        profile.save()

        # 'solar team' is intentionally lower case to test case insensitivity in name ordering
        self.test_team_1 = CourseTeamFactory.create(
            name=u'sólar team',
            course_id=self.test_course_1.id,
            topic_id='topic_0')
        self.test_team_2 = CourseTeamFactory.create(
            name='Wind Team', course_id=self.test_course_1.id)
        self.test_team_3 = CourseTeamFactory.create(
            name='Nuclear Team', course_id=self.test_course_1.id)
        self.test_team_4 = CourseTeamFactory.create(
            name='Coal Team', course_id=self.test_course_1.id, is_active=False)
        self.test_team_5 = CourseTeamFactory.create(
            name='Another Team', course_id=self.test_course_2.id)
        self.test_team_6 = CourseTeamFactory.create(
            name='Public Profile Team',
            course_id=self.test_course_2.id,
            topic_id='topic_6')

        self.test_team_name_id_map = {
            team.name: team
            for team in (
                self.test_team_1,
                self.test_team_2,
                self.test_team_3,
                self.test_team_4,
                self.test_team_5,
            )
        }

        for user, course in [('staff', self.test_course_1),
                             ('course_staff', self.test_course_1)]:
            CourseEnrollment.enroll(self.users[user],
                                    course.id,
                                    check_access=True)

        self.test_team_1.add_user(self.users['student_enrolled'])
        self.test_team_3.add_user(
            self.users['student_enrolled_both_courses_other_team'])
        self.test_team_5.add_user(
            self.users['student_enrolled_both_courses_other_team'])
        self.test_team_6.add_user(
            self.users['student_enrolled_public_profile'])
示例#54
0
 def test_with_invalid_course_id(self):
     CourseEnrollment.enroll(self.user, self.course.id, mode="honor")
     resp = self._change_enrollment('unenroll', course_id="edx/")
     self.assertEqual(resp.status_code, 400)
示例#55
0
def auto_auth(request):
    """
    Create or configure a user account, then log in as that user.

    Enabled only when
    settings.FEATURES['AUTOMATIC_AUTH_FOR_TESTING'] is true.

    Accepts the following querystring parameters:
    * `username`, `email`, and `password` for the user account
    * `full_name` for the user profile (the user's full name; defaults to the username)
    * `staff`: Set to "true" to make the user global staff.
    * `course_id`: Enroll the student in the course with `course_id`
    * `roles`: Comma-separated list of roles to grant the student in the course with `course_id`
    * `no_login`: Define this to create the user but not login
    * `redirect`: Set to "true" will redirect to the `redirect_to` value if set, or
        course home page if course_id is defined, otherwise it will redirect to dashboard
    * `redirect_to`: will redirect to to this url
    * `is_active` : make/update account with status provided as 'is_active'
    If username, email, or password are not provided, use
    randomly generated credentials.
    """

    # Generate a unique name to use if none provided
    generated_username = uuid.uuid4().hex[0:30]
    generated_password = generate_password()

    # Use the params from the request, otherwise use these defaults
    username = request.GET.get('username', generated_username)
    password = request.GET.get('password', generated_password)
    email = request.GET.get('email', username + "@example.com")
    full_name = request.GET.get('full_name', username)
    is_staff = str2bool(request.GET.get('staff', False))
    is_superuser = str2bool(request.GET.get('superuser', False))
    course_id = request.GET.get('course_id')
    redirect_to = request.GET.get('redirect_to')
    is_active = str2bool(request.GET.get('is_active', True))

    # Valid modes: audit, credit, honor, no-id-professional, professional, verified
    enrollment_mode = request.GET.get('enrollment_mode', 'honor')

    # Parse roles, stripping whitespace, and filtering out empty strings
    roles = _clean_roles(request.GET.get('roles', '').split(','))
    course_access_roles = _clean_roles(
        request.GET.get('course_access_roles', '').split(','))

    redirect_when_done = str2bool(request.GET.get('redirect',
                                                  '')) or redirect_to
    login_when_done = 'no_login' not in request.GET

    form = AccountCreationForm(data={
        'username': username,
        'email': email,
        'password': password,
        'name': full_name,
    },
                               tos_required=False)

    # Attempt to create the account.
    # If successful, this will return a tuple containing
    # the new user object.
    try:
        user, profile, reg = do_create_account(form)
    except (AccountValidationError, ValidationError):
        # Attempt to retrieve the existing user.
        user = User.objects.get(username=username)
        user.email = email
        user.set_password(password)
        user.is_active = is_active
        user.save()
        profile = UserProfile.objects.get(user=user)
        reg = Registration.objects.get(user=user)
    except PermissionDenied:
        return HttpResponseForbidden(_('Account creation not allowed.'))

    user.is_staff = is_staff
    user.is_superuser = is_superuser
    user.save()

    if is_active:
        reg.activate()
        reg.save()

    # ensure parental consent threshold is met
    year = datetime.date.today().year
    age_limit = settings.PARENTAL_CONSENT_AGE_LIMIT
    profile.year_of_birth = (year - age_limit) - 1
    profile.save()

    create_or_set_user_attribute_created_on_site(user, request.site)

    # Enroll the user in a course
    course_key = None
    if course_id:
        course_key = CourseLocator.from_string(course_id)
        CourseEnrollment.enroll(user, course_key, mode=enrollment_mode)

        # Apply the roles
        for role in roles:
            assign_role(course_key, user, role)

        for role in course_access_roles:
            CourseAccessRole.objects.update_or_create(user=user,
                                                      course_id=course_key,
                                                      org=course_key.org,
                                                      role=role)

    # Log in as the user
    if login_when_done:
        user = authenticate_new_user(request, username, password)
        django_login(request, user)

    create_comments_service_user(user)

    if redirect_when_done:
        if redirect_to:
            # Redirect to page specified by the client
            redirect_url = redirect_to
        elif course_id:
            # Redirect to the course homepage (in LMS) or outline page (in Studio)
            try:
                redirect_url = reverse(course_home_url_name(course_key),
                                       kwargs={'course_id': course_id})
            except NoReverseMatch:
                redirect_url = reverse('course_handler',
                                       kwargs={'course_key_string': course_id})
        else:
            # Redirect to the learner dashboard (in LMS) or homepage (in Studio)
            try:
                redirect_url = reverse('dashboard')
            except NoReverseMatch:
                redirect_url = reverse('home')

        return redirect(redirect_url)
    else:
        response = JsonResponse({
            'created_status':
            'Logged in' if login_when_done else 'Created',
            'username':
            username,
            'email':
            email,
            'password':
            password,
            'user_id':
            user.id,  # pylint: disable=no-member
            'anonymous_id':
            anonymous_id_for_user(user, None),
        })
    response.set_cookie('csrftoken', csrf(request)['csrf_token'])
    return response
示例#56
0
def change_enrollment(request, check_access=True):
    """
    Modify the enrollment status for the logged-in user.

    TODO: This is lms specific and does not belong in common code.

    The request parameter must be a POST request (other methods return 405)
    that specifies course_id and enrollment_action parameters. If course_id or
    enrollment_action is not specified, if course_id is not valid, if
    enrollment_action is something other than "enroll" or "unenroll", if
    enrollment_action is "enroll" and enrollment is closed for the course, or
    if enrollment_action is "unenroll" and the user is not enrolled in the
    course, a 400 error will be returned. If the user is not logged in, 403
    will be returned; it is important that only this case return 403 so the
    front end can redirect the user to a registration or login page when this
    happens. This function should only be called from an AJAX request, so
    the error messages in the responses should never actually be user-visible.

    Args:
        request (`Request`): The Django request object

    Keyword Args:
        check_access (boolean): If True, we check that an accessible course actually
            exists for the given course_key before we enroll the student.
            The default is set to False to avoid breaking legacy code or
            code with non-standard flows (ex. beta tester invitations), but
            for any standard enrollment flow you probably want this to be True.

    Returns:
        Response

    """
    # Get the user
    user = request.user

    # Ensure the user is authenticated
    if not user.is_authenticated:
        return HttpResponseForbidden()

    # Ensure we received a course_id
    action = request.POST.get("enrollment_action")
    if 'course_id' not in request.POST:
        return HttpResponseBadRequest(_("Course id not specified"))

    try:
        course_id = CourseKey.from_string(request.POST.get("course_id"))
    except InvalidKeyError:
        log.warning(
            u"User %s tried to %s with invalid course id: %s",
            user.username,
            action,
            request.POST.get("course_id"),
        )
        return HttpResponseBadRequest(_("Invalid course id"))

    # Allow us to monitor performance of this transaction on a per-course basis since we often roll-out features
    # on a per-course basis.
    monitoring_utils.set_custom_metric('course_id', text_type(course_id))

    if action == "enroll":
        # Make sure the course exists
        # We don't do this check on unenroll, or a bad course id can't be unenrolled from
        if not modulestore().has_course(course_id):
            log.warning(u"User %s tried to enroll in non-existent course %s",
                        user.username, course_id)
            return HttpResponseBadRequest(_("Course id is invalid"))

        # Record the user's email opt-in preference
        if settings.FEATURES.get('ENABLE_MKTG_EMAIL_OPT_IN'):
            _update_email_opt_in(request, course_id.org)

        available_modes = CourseMode.modes_for_course_dict(course_id)

        # Check whether the user is blocked from enrolling in this course
        # This can occur if the user's IP is on a global blacklist
        # or if the user is enrolling in a country in which the course
        # is not available.
        redirect_url = embargo_api.redirect_if_blocked(
            course_id, user=user, ip_address=get_ip(request), url=request.path)
        if redirect_url:
            return HttpResponse(redirect_url)

        if CourseEntitlement.check_for_existing_entitlement_and_enroll(
                user=user, course_run_key=course_id):
            return HttpResponse(
                reverse('courseware', args=[six.text_type(course_id)]))

        # Check that auto enrollment is allowed for this course
        # (= the course is NOT behind a paywall)
        if CourseMode.can_auto_enroll(course_id):
            # Enroll the user using the default mode (audit)
            # We're assuming that users of the course enrollment table
            # will NOT try to look up the course enrollment model
            # by its slug.  If they do, it's possible (based on the state of the database)
            # for no such model to exist, even though we've set the enrollment type
            # to "audit".
            try:
                enroll_mode = CourseMode.auto_enroll_mode(
                    course_id, available_modes)
                if enroll_mode:
                    CourseEnrollment.enroll(user,
                                            course_id,
                                            check_access=check_access,
                                            mode=enroll_mode)
            except Exception:  # pylint: disable=broad-except
                return HttpResponseBadRequest(_("Could not enroll"))

        # If we have more than one course mode or professional ed is enabled,
        # then send the user to the choose your track page.
        # (In the case of no-id-professional/professional ed, this will redirect to a page that
        # funnels users directly into the verification / payment flow)
        if CourseMode.has_verified_mode(
                available_modes) or CourseMode.has_professional_mode(
                    available_modes):
            return HttpResponse(
                reverse("course_modes_choose",
                        kwargs={'course_id': text_type(course_id)}))

        # Otherwise, there is only one mode available (the default)
        return HttpResponse()
    elif action == "unenroll":
        enrollment = CourseEnrollment.get_enrollment(user, course_id)
        if not enrollment:
            return HttpResponseBadRequest(
                _("You are not enrolled in this course"))

        certificate_info = cert_info(user, enrollment.course_overview)
        if certificate_info.get('status') in DISABLE_UNENROLL_CERT_STATES:
            return HttpResponseBadRequest(
                _("Your certificate prevents you from unenrolling from this course"
                  ))

        CourseEnrollment.unenroll(user, course_id)
        REFUND_ORDER.send(sender=None, course_enrollment=enrollment)
        return HttpResponse()
    else:
        return HttpResponseBadRequest(_("Enrollment action is invalid"))
示例#57
0
 def test_add_course_to_cart_already_registered(self):
     CourseEnrollment.enroll(self.user, self.course_key)
     self.login_user()
     resp = self.client.post(reverse('shoppingcart.views.add_course_to_cart', args=[self.course_key.to_deprecated_string()]))
     self.assertEqual(resp.status_code, 400)
     self.assertIn(_('You are already registered in course {0}.'.format(self.course_key.to_deprecated_string())), resp.content)
    def test_unenroll(self):
        CourseEnrollment.enroll(self.student, self.course.id)
        self.update_enrollement("unenroll", self.student.email)

        self.check_outbox_is_french()
 def setUpTestData(cls):
     """Set up and enroll our fake user in the course."""
     cls.user = UserFactory(password=TEST_PASSWORD)
     CourseEnrollment.enroll(cls.user, cls.course.id)
     cls.site = Site.objects.get_current()
def create_new_course(request):
    """
    Create a new course.

    Returns the URL for the course overview page.
    """
    if not auth.has_access(request.user, CourseCreatorRole()):
        raise PermissionDenied()

    org = request.json.get('org')
    number = request.json.get('number')
    display_name = request.json.get('display_name')
    run = request.json.get('run')

    # allow/disable unicode characters in course_id according to settings
    if not settings.FEATURES.get('ALLOW_UNICODE_COURSE_ID'):
        if _has_non_ascii_characters(org) or _has_non_ascii_characters(number) or _has_non_ascii_characters(run):
            return JsonResponse(
                {'error': _('Special characters not allowed in organization, course number, and course run.')},
                status=400
            )

    try:
        course_key = SlashSeparatedCourseKey(org, number, run)

        # instantiate the CourseDescriptor and then persist it
        # note: no system to pass
        if display_name is None:
            metadata = {}
        else:
            metadata = {'display_name': display_name}

        # Set a unique wiki_slug for newly created courses. To maintain active wiki_slugs for
        # existing xml courses this cannot be changed in CourseDescriptor.
        # # TODO get rid of defining wiki slug in this org/course/run specific way and reconcile
        # w/ xmodule.course_module.CourseDescriptor.__init__
        wiki_slug = u"{0}.{1}.{2}".format(course_key.org, course_key.course, course_key.run)
        definition_data = {'wiki_slug': wiki_slug}

        # Create the course then fetch it from the modulestore
        # Check if role permissions group for a course named like this already exists
        # Important because role groups are case insensitive
        if CourseRole.course_group_already_exists(course_key):
            raise InvalidLocationError()

        fields = {}
        fields.update(definition_data)
        fields.update(metadata)

        # Creating the course raises InvalidLocationError if an existing course with this org/name is found
        new_course = modulestore('direct').create_course(
            course_key.org,
            course_key.offering,
            fields=fields,
        )
	
        # can't use auth.add_users here b/c it requires request.user to already have Instructor perms in this course
        # however, we can assume that b/c this user had authority to create the course, the user can add themselves
        CourseInstructorRole(new_course.id).add_users(request.user)
        auth.add_users(request.user, CourseStaffRole(new_course.id), request.user)

        # seed the forums
        seed_permissions_roles(new_course.id)

        # auto-enroll the course creator in the course so that "View Live" will
        # work.
        CourseEnrollment.enroll(request.user, new_course.id)
        _users_assign_default_role(new_course.id)
	#os.system('python /home/rajarshi/trigger/trigger_test.py')

        return JsonResponse({
            'url': reverse_course_url('course_handler', new_course.id)
        })

    except InvalidLocationError:
        return JsonResponse({
            'ErrMsg': _(
                'There is already a course defined with the same '
                'organization, course number, and course run. Please '
                'change either organization or course number to be unique.'
            ),
            'OrgErrMsg': _(
                'Please change either the organization or '
                'course number so that it is unique.'),
            'CourseErrMsg': _(
                'Please change either the organization or '
                'course number so that it is unique.'),
        })
    except InvalidKeyError as error:
        return JsonResponse({
            "ErrMsg": _("Unable to create course '{name}'.\n\n{err}").format(name=display_name, err=error.message)}
        )