Esempio n. 1
0
    def test_enrollment(self):
        user = User.objects.create_user("joe", "*****@*****.**", "password")
        course_id = "edX/Test101/2013"
        course_id_partial = "edX/Test101"

        # Test basic enrollment
        self.assertFalse(CourseEnrollment.is_enrolled(user, course_id))
        self.assertFalse(CourseEnrollment.is_enrolled_by_partial(user, course_id_partial))
        CourseEnrollment.enroll(user, course_id)
        self.assertTrue(CourseEnrollment.is_enrolled(user, course_id))
        self.assertTrue(CourseEnrollment.is_enrolled_by_partial(user, course_id_partial))

        # Enrolling them again should be harmless
        CourseEnrollment.enroll(user, course_id)
        self.assertTrue(CourseEnrollment.is_enrolled(user, course_id))
        self.assertTrue(CourseEnrollment.is_enrolled_by_partial(user, course_id_partial))

        # Now unenroll the user
        CourseEnrollment.unenroll(user, course_id)
        self.assertFalse(CourseEnrollment.is_enrolled(user, course_id))
        self.assertFalse(CourseEnrollment.is_enrolled_by_partial(user, course_id_partial))

        # Unenrolling them again should also be harmless
        CourseEnrollment.unenroll(user, course_id)
        self.assertFalse(CourseEnrollment.is_enrolled(user, course_id))
        self.assertFalse(CourseEnrollment.is_enrolled_by_partial(user, course_id_partial))

        # The enrollment record should still exist, just be inactive
        enrollment_record = CourseEnrollment.objects.get(user=user, course_id=course_id)
        self.assertFalse(enrollment_record.is_active)
Esempio n. 2
0
    def test_enroll(self, course_modes, next_url, enrollment_mode, auto_reg):
        # Create the course modes (if any) required for this test case
        for mode_slug in course_modes:
            CourseModeFactory.create(course_id=self.course.id, mode_slug=mode_slug, mode_display_name=mode_slug)

        # Reverse the expected next URL, if one is provided
        # (otherwise, use an empty string, which the JavaScript client
        # interprets as a redirect to the dashboard)
        full_url = reverse(next_url, kwargs={"course_id": unicode(self.course.id)}) if next_url else next_url

        # Enroll in the course and verify the URL we get sent to
        resp = self._change_enrollment("enroll", auto_reg=auto_reg)
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content, full_url)

        # TODO (ECOM-16): If auto-registration is enabled, check that we're
        # storing the auto-reg flag in the user's session
        if auto_reg:
            self.assertIn("auto_register", self.client.session)
            self.assertTrue(self.client.session["auto_register"])

        # If we're not expecting to be enrolled, verify that this is the case
        if enrollment_mode is None:
            self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course.id))

        # Otherwise, verify that we're enrolled with the expected course mode
        else:
            self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course.id))
            course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
            self.assertTrue(is_active)
            self.assertEqual(course_mode, enrollment_mode)
Esempio n. 3
0
    def test_user_can_unenroll(self, mock_get_course_runs):
        course_entitlement = CourseEntitlementFactory.create(user=self.user, mode=CourseMode.VERIFIED)
        mock_get_course_runs.return_value = self.return_values

        url = reverse(
            self.ENTITLEMENTS_ENROLLMENT_NAMESPACE,
            args=[str(course_entitlement.uuid)]
        )
        assert course_entitlement.enrollment_course_run is None

        data = {
            'course_run_id': str(self.course.id)
        }
        response = self.client.post(
            url,
            data=json.dumps(data),
            content_type='application/json',
        )
        course_entitlement.refresh_from_db()

        assert response.status_code == 201
        assert CourseEnrollment.is_enrolled(self.user, self.course.id)

        response = self.client.delete(
            url,
            content_type='application/json',
        )
        assert response.status_code == 204

        course_entitlement.refresh_from_db()
        assert not CourseEnrollment.is_enrolled(self.user, self.course.id)
        assert course_entitlement.enrollment_course_run is None
Esempio n. 4
0
    def test_enrollment_by_email(self):
        user = User.objects.create(username="******", email="*****@*****.**")
        course_id = SlashSeparatedCourseKey("edX", "Test101", "2013")

        CourseEnrollment.enroll_by_email("*****@*****.**", course_id)
        self.assertTrue(CourseEnrollment.is_enrolled(user, course_id))
        self.assert_enrollment_event_was_emitted(user, course_id)

        # This won't throw an exception, even though the user is not found
        self.assertIsNone(CourseEnrollment.enroll_by_email("*****@*****.**", course_id))
        self.assert_no_events_were_emitted()

        self.assertRaises(
            User.DoesNotExist, CourseEnrollment.enroll_by_email, "*****@*****.**", course_id, ignore_errors=False
        )
        self.assert_no_events_were_emitted()

        # Now unenroll them by email
        CourseEnrollment.unenroll_by_email("*****@*****.**", course_id)
        self.assertFalse(CourseEnrollment.is_enrolled(user, course_id))
        self.assert_unenrollment_event_was_emitted(user, course_id)

        # Harmless second unenroll
        CourseEnrollment.unenroll_by_email("*****@*****.**", course_id)
        self.assertFalse(CourseEnrollment.is_enrolled(user, course_id))
        self.assert_no_events_were_emitted()

        # Unenroll on non-existent user shouldn't throw an error
        CourseEnrollment.unenroll_by_email("*****@*****.**", course_id)
        self.assert_no_events_were_emitted()
    def test_unenrollment_email_off(self):
        """
        Do un-enrollment email off test
        """

        course = self.course

        # Run the Un-enroll students command
        url = reverse('instructor_dashboard_legacy', kwargs={'course_id': course.id.to_deprecated_string()})
        response = self.client.post(
            url,
            {
                'action': 'Unenroll multiple students',
                'multiple_students': '[email protected] [email protected]'
            }
        )

        # Check the page output
        self.assertContains(response, '<td>[email protected]</td>')
        self.assertContains(response, '<td>[email protected]</td>')
        self.assertContains(response, '<td>un-enrolled</td>')

        # Check the enrollment table
        user = User.objects.get(email='*****@*****.**')
        self.assertFalse(CourseEnrollment.is_enrolled(user, course.id))

        user = User.objects.get(email='*****@*****.**')
        self.assertFalse(CourseEnrollment.is_enrolled(user, course.id))

        # Check the outbox
        self.assertEqual(len(mail.outbox), 0)
Esempio n. 6
0
    def test_check_for_existing_entitlement_and_enroll(self, mock_get_course_uuid):
        course = CourseFactory()
        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=now() + timedelta(days=1)
        )
        entitlement = CourseEntitlementFactory.create(
            mode=CourseMode.VERIFIED,
            user=self.user,
        )
        mock_get_course_uuid.return_value = entitlement.course_uuid

        assert not CourseEnrollment.is_enrolled(user=self.user, course_key=course.id)

        CourseEntitlement.check_for_existing_entitlement_and_enroll(
            user=self.user,
            course_run_key=course.id,
        )

        assert CourseEnrollment.is_enrolled(user=self.user, course_key=course.id)

        entitlement.refresh_from_db()
        assert entitlement.enrollment_course_run
Esempio n. 7
0
    def test_enroll(self, course_modes, next_url, enrollment_mode):
        # Create the course modes (if any) required for this test case
        for mode_slug in course_modes:
            CourseModeFactory.create(
                course_id=self.course.id,
                mode_slug=mode_slug,
                mode_display_name=mode_slug,
            )

        # Reverse the expected next URL, if one is provided
        # (otherwise, use an empty string, which the JavaScript client
        # interprets as a redirect to the dashboard)
        full_url = (
            reverse(next_url, kwargs={'course_id': unicode(self.course.id)})
            if next_url else next_url
        )

        # Enroll in the course and verify the URL we get sent to
        resp = self._change_enrollment('enroll')
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content, full_url)

        # If we're not expecting to be enrolled, verify that this is the case
        if enrollment_mode is None:
            self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course.id))

        # Otherwise, verify that we're enrolled with the expected course mode
        else:
            self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course.id))
            course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
            self.assertTrue(is_active)
            self.assertEqual(course_mode, enrollment_mode)
    def test_unenrollment_email_off(self):
        """
        Do un-enrollment email off test
        """

        course = self.course

        # Run the Un-enroll students command
        url = reverse("instructor_dashboard", kwargs={"course_id": course.id})
        response = self.client.post(
            url, {"action": "Unenroll multiple students", "multiple_students": "[email protected] [email protected]"}
        )

        # Check the page output
        self.assertContains(response, "<td>[email protected]</td>")
        self.assertContains(response, "<td>[email protected]</td>")
        self.assertContains(response, "<td>un-enrolled</td>")

        # Check the enrollment table
        user = User.objects.get(email="*****@*****.**")
        self.assertFalse(CourseEnrollment.is_enrolled(user, course.id))

        user = User.objects.get(email="*****@*****.**")
        self.assertFalse(CourseEnrollment.is_enrolled(user, course.id))

        # Check the outbox
        self.assertEqual(len(mail.outbox), 0)
Esempio n. 9
0
    def test_shib_login_enrollment(self):
        """
            A functionality test that a student with an existing shib login
            can auto-enroll in a class with GET or POST params.  Also tests the direction functionality of
            the 'next' GET/POST param
        """
        student = UserFactory.create()
        extauth = ExternalAuthMap(
            external_id="*****@*****.**",
            external_email="",
            external_domain="shib:https://idp.stanford.edu/",
            external_credentials="",
            internal_password="******",
            user=student,
        )
        student.set_password("password")
        student.save()
        extauth.save()

        course = CourseFactory.create(
            org="Stanford",
            number="123",
            display_name="Shib Only",
            enrollment_domain="shib:https://idp.stanford.edu/",
            user_id=self.test_user_id,
        )

        # use django test client for sessions and url processing
        # no enrollment before trying
        self.assertFalse(CourseEnrollment.is_enrolled(student, course.id))
        self.client.logout()
        request_kwargs = {
            "path": "/shib-login/",
            "data": {
                "enrollment_action": "enroll",
                "course_id": course.id.to_deprecated_string(),
                "next": "/testredirect",
            },
            "follow": False,
            "REMOTE_USER": "******",
            "Shib-Identity-Provider": "https://idp.stanford.edu/",
        }
        response = self.client.get(**request_kwargs)
        # successful login is a redirect to "/"
        self.assertEqual(response.status_code, 302)
        self.assertEqual(response["location"], "http://testserver/testredirect")
        # now there is enrollment
        self.assertTrue(CourseEnrollment.is_enrolled(student, course.id))

        # Clean up and try again with POST (doesn't happen with real production shib, doing this for test coverage)
        self.client.logout()
        CourseEnrollment.unenroll(student, course.id)
        self.assertFalse(CourseEnrollment.is_enrolled(student, course.id))

        response = self.client.post(**request_kwargs)
        # successful login is a redirect to "/"
        self.assertEqual(response.status_code, 302)
        self.assertEqual(response["location"], "http://testserver/testredirect")
        # now there is enrollment
        self.assertTrue(CourseEnrollment.is_enrolled(student, course.id))
Esempio n. 10
0
    def test_enrollment_limit_by_domain(self):
        """
            Tests that the enrollmentDomain setting is properly limiting enrollment to those who have
            the proper external auth
        """

        # create 2 course, one with limited enrollment one without
        shib_course = CourseFactory.create(org='Stanford', number='123', display_name='Shib Only')
        shib_course.enrollment_domain = 'shib:https://idp.stanford.edu/'
        self.store.update_item(shib_course, '**replace_user**')

        open_enroll_course = CourseFactory.create(org='MITx', number='999', display_name='Robot Super Course')
        open_enroll_course.enrollment_domain = ''
        self.store.update_item(open_enroll_course, '**replace_user**')

        # create 3 kinds of students, external_auth matching shib_course, external_auth not matching, no external auth
        shib_student = UserFactory.create()
        shib_student.save()
        extauth = ExternalAuthMap(external_id='*****@*****.**',
                                  external_email='',
                                  external_domain='shib:https://idp.stanford.edu/',
                                  external_credentials="",
                                  user=shib_student)
        extauth.save()

        other_ext_student = UserFactory.create()
        other_ext_student.username = "******"
        other_ext_student.email = "*****@*****.**"
        other_ext_student.save()
        extauth = ExternalAuthMap(external_id='*****@*****.**',
                                  external_email='',
                                  external_domain='shib:https://other.edu/',
                                  external_credentials="",
                                  user=other_ext_student)
        extauth.save()

        int_student = UserFactory.create()
        int_student.username = "******"
        int_student.email = "*****@*****.**"
        int_student.save()

        # Tests the two case for courses, limited and not
        for course in [shib_course, open_enroll_course]:
            for student in [shib_student, other_ext_student, int_student]:
                request = self.request_factory.post('/change_enrollment')
                request.POST.update({'enrollment_action': 'enroll',
                                     'course_id': course.id.to_deprecated_string()})
                request.user = student
                response = change_enrollment(request)
                # If course is not limited or student has correct shib extauth then enrollment should be allowed
                if course is open_enroll_course or student is shib_student:
                    self.assertEqual(response.status_code, 200)
                    self.assertTrue(CourseEnrollment.is_enrolled(student, course.id))
                    # Clean up
                    CourseEnrollment.unenroll(student, course.id)
                else:
                    self.assertEqual(response.status_code, 400)
                    self.assertFalse(CourseEnrollment.is_enrolled(student, course.id))
    def test_enrollment_new_student_autoenroll_on_email_off(self):
        """
        Do auto-enroll on, email off test
        """

        course = self.course

        # Run the Enroll students command
        url = reverse("instructor_dashboard_legacy", kwargs={"course_id": course.id.to_deprecated_string()})
        response = self.client.post(
            url,
            {
                "action": "Enroll multiple students",
                "multiple_students": "[email protected], [email protected]",
                "auto_enroll": "on",
            },
        )

        # Check the page output
        self.assertContains(response, "<td>[email protected]</td>")
        self.assertContains(response, "<td>[email protected]</td>")
        self.assertContains(
            response, "<td>user does not exist, enrollment allowed, pending with auto enrollment on</td>"
        )

        # Check the outbox
        self.assertEqual(len(mail.outbox), 0)

        # Check the enrollmentallowed db entries
        cea = CourseEnrollmentAllowed.objects.filter(email="*****@*****.**", course_id=course.id)
        self.assertEqual(1, cea[0].auto_enroll)
        cea = CourseEnrollmentAllowed.objects.filter(email="*****@*****.**", course_id=course.id)
        self.assertEqual(1, cea[0].auto_enroll)

        # Check there is no enrollment db entry other than for the other students
        ce = CourseEnrollment.objects.filter(course_id=course.id, is_active=1)
        self.assertEqual(4, len(ce))

        # Create and activate student accounts with same email
        self.student1 = "*****@*****.**"
        self.password = "******"
        self.create_account("s1_1", self.student1, self.password)
        self.activate_user(self.student1)

        self.student2 = "*****@*****.**"
        self.create_account("s1_2", self.student2, self.password)
        self.activate_user(self.student2)

        # Check students are enrolled
        user = User.objects.get(email="*****@*****.**")
        self.assertTrue(CourseEnrollment.is_enrolled(user, course.id))

        user = User.objects.get(email="*****@*****.**")
        self.assertTrue(CourseEnrollment.is_enrolled(user, course.id))
Esempio n. 12
0
 def test_purchase(self):
     # This test is for testing the subclassing functionality of OrderItem, but in
     # order to do this, we end up testing the specific functionality of
     # CertificateItem, which is not quite good unit test form. Sorry.
     cart = Order.get_cart_for_user(user=self.user)
     self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course_id))
     CertificateItem.add_to_order(cart, self.course_id, self.cost, 'verified')
     # course enrollment object should be created but still inactive
     self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course_id))
     cart.purchase()
     self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course_id))
Esempio n. 13
0
    def test_unregister_students(self):
        users = UserFactory.create_batch(size=20)
        self.enroll_students(self.course, *users)
        for user in users:
            self.assertTrue(CourseEnrollment.is_enrolled(user, self.course.id))
        input_file = self.create_input_file(*[user.email for user in users])

        self.unregister_student(input_file)

        for user in users:
            self.assertFalse(CourseEnrollment.is_enrolled(user, self.course.id))
Esempio n. 14
0
 def login_with_enrollment(self):
     course = CourseFactory.create()
     user = UserFactory.create()
     self.assertFalse(CourseEnrollment.is_enrolled(user, course.id))
     post_params = {
         'email': user.email,
         'password': '******',
         'enrollment_action': 'enroll',
         'course_id': unicode(course.id)
     }
     self.client.post(reverse('login'), post_params)
     self.assertTrue(CourseEnrollment.is_enrolled(user, course.id))
Esempio n. 15
0
    def test_shib_login_enrollment(self):
        """
            A functionality test that a student with an existing shib login
            can auto-enroll in a class with GET or POST params.  Also tests the direction functionality of
            the 'next' GET/POST param
        """
        student = UserFactory.create()
        extauth = ExternalAuthMap(external_id='*****@*****.**',
                                  external_email='',
                                  external_domain='shib:https://idp.stanford.edu/',
                                  external_credentials="",
                                  internal_password="******",
                                  user=student)
        student.set_password("password")
        student.save()
        extauth.save()

        course = CourseFactory.create(
            org='Stanford',
            number='123',
            display_name='Shib Only',
            enrollment_domain='shib:https://idp.stanford.edu/',
            user_id=self.test_user_id,
        )

        # use django test client for sessions and url processing
        # no enrollment before trying
        self.assertFalse(CourseEnrollment.is_enrolled(student, course.id))
        self.client.logout()
        request_kwargs = {'path': '/shib-login/',
                          'data': {'enrollment_action': 'enroll', 'course_id': course.id.to_deprecated_string(), 'next': '/testredirect'},
                          'follow': False,
                          'REMOTE_USER': '******',
                          'Shib-Identity-Provider': 'https://idp.stanford.edu/'}
        response = self.client.get(**request_kwargs)
        # successful login is a redirect to "/"
        self.assertEqual(response.status_code, 302)
        self.assertEqual(response['location'], 'http://testserver/testredirect')
        # now there is enrollment
        self.assertTrue(CourseEnrollment.is_enrolled(student, course.id))

        # Clean up and try again with POST (doesn't happen with real production shib, doing this for test coverage)
        self.client.logout()
        CourseEnrollment.unenroll(student, course.id)
        self.assertFalse(CourseEnrollment.is_enrolled(student, course.id))

        response = self.client.post(**request_kwargs)
        # successful login is a redirect to "/"
        self.assertEqual(response.status_code, 302)
        self.assertEqual(response['location'], 'http://testserver/testredirect')
        # now there is enrollment
        self.assertTrue(CourseEnrollment.is_enrolled(student, course.id))
Esempio n. 16
0
    def test_purchased_callback_exception(self):
        reg1 = PaidCourseRegistration.add_to_order(self.cart, self.course_key)
        reg1.course_id = CourseLocator(org="changed", course="forsome", run="reason")
        reg1.save()
        with self.assertRaises(PurchasedCallbackException):
            reg1.purchased_callback()
        self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course_key))

        reg1.course_id = CourseLocator(org="abc", course="efg", run="hij")
        reg1.save()
        with self.assertRaises(PurchasedCallbackException):
            reg1.purchased_callback()
        self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course_key))
Esempio n. 17
0
    def test_purchased_callback_exception(self):
        reg1 = PaidCourseRegistration.add_to_order(self.cart, self.course_key)
        reg1.course_id = SlashSeparatedCourseKey("changed", "forsome", "reason")
        reg1.save()
        with self.assertRaises(PurchasedCallbackException):
            reg1.purchased_callback()
        self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course_key))

        reg1.course_id = SlashSeparatedCourseKey("abc", "efg", "hij")
        reg1.save()
        with self.assertRaises(PurchasedCallbackException):
            reg1.purchased_callback()
        self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course_key))
Esempio n. 18
0
    def test_purchased_callback_exception(self):
        reg1 = PaidCourseRegistration.add_to_order(self.cart, self.course_id)
        reg1.course_id = "changedforsomereason"
        reg1.save()
        with self.assertRaises(PurchasedCallbackException):
            reg1.purchased_callback()
        self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course_id))

        reg1.course_id = "abc/efg/hij"
        reg1.save()
        with self.assertRaises(PurchasedCallbackException):
            reg1.purchased_callback()
        self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course_id))
Esempio n. 19
0
    def test_enrollment_non_existent_user(self):
        # Testing enrollment of newly unsaved user (i.e. no database entry)
        user = User(username="******", email="*****@*****.**")
        course_id = "edX/Test101/2013"

        self.assertFalse(CourseEnrollment.is_enrolled(user, course_id))

        # Unenroll does nothing
        CourseEnrollment.unenroll(user, course_id)

        # Implicit save() happens on new User object when enrolling, so this
        # should still work
        CourseEnrollment.enroll(user, course_id)
        self.assertTrue(CourseEnrollment.is_enrolled(user, course_id))
Esempio n. 20
0
    def test_enrollment_non_existent_user(self):
        # Testing enrollment of newly unsaved user (i.e. no database entry)
        user = User(username="******", email="*****@*****.**")
        course_id = SlashSeparatedCourseKey("edX", "Test101", "2013")

        self.assertFalse(CourseEnrollment.is_enrolled(user, course_id))

        # Unenroll does nothing
        CourseEnrollment.unenroll(user, course_id)
        self.assert_no_events_were_emitted()

        # Implicit save() happens on new User object when enrolling, so this
        # should still work
        CourseEnrollment.enroll(user, course_id)
        self.assertTrue(CourseEnrollment.is_enrolled(user, course_id))
        self.assert_enrollment_event_was_emitted(user, course_id)
Esempio n. 21
0
def get_course_with_access(user, action, course_key, depth=0, check_if_enrolled=False):
    """
    Given a course_key, look up the corresponding course descriptor,
    check that the user has the access to perform the specified action
    on the course, and return the descriptor.

    Raises a 404 if the course_key is invalid, or the user doesn't have access.

    depth: The number of levels of children for the modulestore to cache. None means infinite depth

    check_if_enrolled: If true, additionally verifies that the user is either enrolled in the course
      or has staff access.
    """
    assert isinstance(course_key, CourseKey)
    course = get_course_by_id(course_key, depth=depth)
    access_response = has_access(user, action, course, course_key)

    if not access_response:
        # Deliberately return a non-specific error message to avoid
        # leaking info about access control settings
        raise CoursewareAccessException(access_response)

    if check_if_enrolled:
        # Verify that the user is either enrolled in the course or a staff member.
        # If user is not enrolled, raise UserNotEnrolled exception that will be caught by middleware.
        if not ((user.id and CourseEnrollment.is_enrolled(user, course_key)) or has_access(user, 'staff', course)):
            raise UserNotEnrolled(course_key)

    return course
Esempio n. 22
0
    def test_optin_course(self):
        """
        Make sure student receives course email after opting in.
        """
        url = reverse('change_email_settings')
        response = self.client.post(url, {'course_id': self.course.id, 'receive_emails': 'on'})
        self.assertEquals(json.loads(response.content), {'success': True})

        self.client.logout()

        self.assertTrue(CourseEnrollment.is_enrolled(self.student, self.course.id))

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

        url = reverse('instructor_dashboard', kwargs={'course_id': self.course.id})
        test_email = {
            'action': 'Send email',
            'to_option': 'all',
            'subject': 'test subject for all',
            'message': 'test message for all'
        }
        response = self.client.post(url, test_email)

        self.assertContains(response, "Your email was successfully queued for sending.")

        # Assert that self.student.email in mail.to
        self.assertEqual(len(mail.outbox), 1)
        self.assertEqual(len(mail.outbox[0].to), 1)
        self.assertEquals(mail.outbox[0].to[0], self.student.email)
Esempio n. 23
0
    def test_downgrade_enrollment_with_mode(self):
        """With the right API key, downgrade an existing enrollment with a new mode. """
        # Create an honor and verified mode for a course. This allows an update.
        for mode in [CourseMode.DEFAULT_MODE_SLUG, CourseMode.VERIFIED]:
            CourseModeFactory.create(
                course_id=self.course.id,
                mode_slug=mode,
                mode_display_name=mode,
            )

        # Create a 'verified' enrollment
        self.assert_enrollment_status(as_server=True, mode=CourseMode.VERIFIED)

        # Check that the enrollment is verified.
        self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course.id))
        course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
        self.assertTrue(is_active)
        self.assertEqual(course_mode, CourseMode.VERIFIED)

        # Check that the enrollment was downgraded to the default mode.
        self.assert_enrollment_status(
            as_server=True,
            mode=CourseMode.DEFAULT_MODE_SLUG,
            expected_status=status.HTTP_200_OK
        )
        course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
        self.assertTrue(is_active)
        self.assertEqual(course_mode, CourseMode.DEFAULT_MODE_SLUG)
    def test_optin_course(self):
        """
        Make sure student receives course email after opting in.
        """
        url = reverse('change_email_settings')
        response = self.client.post(url, {'course_id': self.course.id.to_deprecated_string(), 'receive_emails': 'on'})
        self.assertEquals(json.loads(response.content), {'success': True})

        self.client.logout()

        self.assertTrue(CourseEnrollment.is_enrolled(self.student, self.course.id))

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

        test_email = {
            'action': 'Send email',
            'send_to': 'all',
            'subject': 'test subject for all',
            'message': 'test message for all'
        }
        response = self.client.post(self.send_mail_url, test_email)
        self.assertEquals(json.loads(response.content), self.success_content)

        # Assert that self.student.email in mail.to
        self.assertEqual(len(mail.outbox), 1)
        self.assertEqual(len(mail.outbox[0].to), 1)
        self.assertEquals(mail.outbox[0].to[0], self.student.email)
Esempio n. 25
0
    def test_update_enrollment_with_expired_mode_throws_error(self):
        """Verify that if verified mode is expired than it's enrollment cannot be updated. """
        for mode in [CourseMode.DEFAULT_MODE_SLUG, CourseMode.VERIFIED]:
            CourseModeFactory.create(
                course_id=self.course.id,
                mode_slug=mode,
                mode_display_name=mode,
            )

        # Create an enrollment
        self.assert_enrollment_status(as_server=True)

        # Check that the enrollment is the default.
        self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course.id))
        course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
        self.assertTrue(is_active)
        self.assertEqual(course_mode, CourseMode.DEFAULT_MODE_SLUG)

        # Change verified mode expiration.
        mode = CourseMode.objects.get(course_id=self.course.id, mode_slug=CourseMode.VERIFIED)
        mode.expiration_datetime = datetime.datetime(year=1970, month=1, day=1, tzinfo=pytz.utc)
        mode.save()
        self.assert_enrollment_status(
            as_server=True,
            mode=CourseMode.VERIFIED,
            expected_status=status.HTTP_400_BAD_REQUEST
        )
        course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
        self.assertTrue(is_active)
        self.assertEqual(course_mode, CourseMode.DEFAULT_MODE_SLUG)
Esempio n. 26
0
    def test_enrollment_with_credit_mode(self):
        """With the right API key, update an existing enrollment with credit
        mode and set enrollment attributes.
        """
        for mode in [CourseMode.DEFAULT_MODE_SLUG, CourseMode.CREDIT_MODE]:
            CourseModeFactory.create(
                course_id=self.course.id,
                mode_slug=mode,
                mode_display_name=mode,
            )

        # Create an enrollment
        self.assert_enrollment_status(as_server=True)

        # Check that the enrollment is the default.
        self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course.id))
        course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
        self.assertTrue(is_active)
        self.assertEqual(course_mode, CourseMode.DEFAULT_MODE_SLUG)

        # Check that the enrollment upgraded to credit.
        enrollment_attributes = [{
            "namespace": "credit",
            "name": "provider_id",
            "value": "hogwarts",
        }]
        self.assert_enrollment_status(
            as_server=True,
            mode=CourseMode.CREDIT_MODE,
            expected_status=status.HTTP_200_OK,
            enrollment_attributes=enrollment_attributes
        )
        course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
        self.assertTrue(is_active)
        self.assertEqual(course_mode, CourseMode.CREDIT_MODE)
Esempio n. 27
0
    def test_enrollment_with_invalid_attr(self):
        """Check response status is bad request when invalid enrollment
        attributes are passed
        """
        for mode in [CourseMode.DEFAULT_MODE_SLUG, CourseMode.CREDIT_MODE]:
            CourseModeFactory.create(
                course_id=self.course.id,
                mode_slug=mode,
                mode_display_name=mode,
            )

        # Create an enrollment
        self.assert_enrollment_status(as_server=True)

        # Check that the enrollment is the default.
        self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course.id))
        course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
        self.assertTrue(is_active)
        self.assertEqual(course_mode, CourseMode.DEFAULT_MODE_SLUG)

        # Check that the enrollment upgraded to credit.
        enrollment_attributes = [{
            "namespace": "credit",
            "name": "invalid",
            "value": "hogwarts",
        }]
        self.assert_enrollment_status(
            as_server=True,
            mode=CourseMode.CREDIT_MODE,
            expected_status=status.HTTP_400_BAD_REQUEST,
            enrollment_attributes=enrollment_attributes
        )
        course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
        self.assertTrue(is_active)
        self.assertEqual(course_mode, CourseMode.DEFAULT_MODE_SLUG)
Esempio n. 28
0
def supplement_program_data(program_data, user):
    """Supplement program course codes with CourseOverview and CourseEnrollment data.

    Arguments:
        program_data (dict): Representation of a program.
        user (User): The user whose enrollments to inspect.
    """
    for course_code in program_data['course_codes']:
        for run_mode in course_code['run_modes']:
            course_key = CourseKey.from_string(run_mode['course_key'])
            course_overview = CourseOverview.get_from_id(course_key)

            run_mode['course_url'] = reverse('course_root', args=[course_key])
            run_mode['course_image_url'] = course_overview.course_image_url

            human_friendly_format = '%x'
            start_date = course_overview.start or DEFAULT_START_DATE
            end_date = course_overview.end or datetime.datetime.max.replace(tzinfo=pytz.UTC)
            run_mode['start_date'] = start_date.strftime(human_friendly_format)
            run_mode['end_date'] = end_date.strftime(human_friendly_format)

            run_mode['is_enrolled'] = CourseEnrollment.is_enrolled(user, course_key)

            enrollment_start = course_overview.enrollment_start or datetime.datetime.min.replace(tzinfo=pytz.UTC)
            enrollment_end = course_overview.enrollment_end or datetime.datetime.max.replace(tzinfo=pytz.UTC)
            is_enrollment_open = enrollment_start <= timezone.now() < enrollment_end
            run_mode['is_enrollment_open'] = is_enrollment_open

            # TODO: Currently unavailable on LMS.
            run_mode['marketing_url'] = ''

    return program_data
Esempio n. 29
0
    def test_create_ccx(self):
        """
        Create CCX. Follow redirect to coach dashboard, confirm we see
        the coach dashboard for the new CCX.
        """

        self.make_coach()
        url = reverse(
            'create_ccx',
            kwargs={'course_id': unicode(self.course.id)})

        response = self.client.post(url, {'name': 'New CCX'})
        self.assertEqual(response.status_code, 302)
        url = response.get('location')  # pylint: disable=no-member
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)

        # Get the ccx_key
        path = urlparse.urlparse(url).path
        resolver = resolve(path)
        ccx_key = resolver.kwargs['course_id']

        course_key = CourseKey.from_string(ccx_key)

        self.assertTrue(CourseEnrollment.is_enrolled(self.coach, course_key))
        self.assertTrue(re.search('id="ccx-schedule"', response.content))

        # check if the max amount of student that can be enrolled has been overridden
        ccx = CustomCourseForEdX.objects.get()
        course_enrollments = get_override_for_ccx(ccx, self.course, 'max_student_enrollments_allowed')
        self.assertEqual(course_enrollments, settings.CCX_MAX_STUDENTS_ALLOWED)
Esempio n. 30
0
    def render_to_fragment(self, request, course_id=None, **kwargs):
        """
        Fragment to render the course reviews fragment.

        """
        course_key = CourseKey.from_string(course_id)
        course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=False)
        course_url_name = default_course_url_name(course.id)
        course_url = reverse(course_url_name, kwargs={'course_id': unicode(course.id)})

        is_enrolled = CourseEnrollment.is_enrolled(request.user, course.id)

        # Create the fragment
        course_reviews_fragment = CourseReviewsModuleFragmentView().render_to_fragment(
            request,
            course=course,
            **kwargs
        )

        context = {
            'course': course,
            'course_url': course_url,
            'course_reviews_fragment': course_reviews_fragment,
            'is_enrolled': is_enrolled,
        }

        html = render_to_string('course_experience/course-reviews-fragment.html', context)
        return Fragment(html)
Esempio n. 31
0
def cme_world_auth(request, user, courseid):
    # _track_user_login(user, request)
    try:
        #request.session.set_expiry(604800)
        #django_login(request, user, backend='django.contrib.auth.backends.ModelBackend')
        #request.session.set_expiry(604800)
        request.user = user
        user_extra_data = userdetails(user.id)
        user_ext = user_extra_data
        user_detail = getuserfullprofile(user.id)
        course_id = request.data.get('course_id', '')
        course_id = course_id.replace(' ', '+')
        #logs.info('course_id %s',course_id)
        course_key = None
        if course_id:
            course_key = CourseKey.from_string(course_id)
            if not CourseEnrollment.is_enrolled(user, course_key):
                #logs.info("course enrollment function will be call")
                CourseEnrollment.enroll(user=user,
                                        course_key=course_key,
                                        mode='honor')

        redirect_url = 'https://shalina.learn.docmode.org/courses/' + course_id + '/courseware'
        logs.debug("Setting user session to never expire")
        missing_data = 0
        if (user_ext.user_extra_data != ""
                and "{" in user_ext.user_extra_data):
            extd = ast.literal_eval(user_ext.user_extra_data)
        else:
            extd = {}
        ext = json.dumps(extd)
        ext = eval(ext)

        name = user_detail.name.split(" ")
        fname = name[0]

        if len(name) > 1:
            lname = name[1]
        else:
            missing_data = 1
            lname = "NA"

        if ext.get("isdcode"):
            isdcode = ext["isdcode"]
        elif ext.get("country_code"):
            isdcode = ext.get("country_code")
        else:
            isdcode = "NA"

        if ext.get("placeofwork"):
            placeofwork = ext["placeofwork"]
        elif ext.get("place_of_work"):
            placeofwork = ext.get("place_of_work")
        else:
            placeofwork = "NA"
            missing_data = 1

        profession = ext.get("profession")
        if profession:
            profession = ext["profession"]
        else:
            profession = "NA"
            missing_data = 1

        othercountry = ext.get("othercountry")
        if othercountry:
            othercountry = ext["othercountry"]
        else:
            missing_data = 1
            othercountry = "NA"

        otherisd = ext.get("otherisd")
        if otherisd:
            otherisd = ext["otherisd"]
        else:
            missing_data = 1
            otherisd = "NA"

        logs.info('before if-----> ')
        if missing_data == 1:
            logs.info('after if-----> ')
            return Response({
                'success': True,
                'fname': fname,
                'lname': lname,
                'email': user.email,
                'isdcode': isdcode,
                'phone': user_ext.phone,
                'country': user_ext.rcountry,
                'city': user_ext.rcity,
                'placeofwork': placeofwork,
                'profession': profession,
                'othercountry': othercountry,
                'otherisd': otherisd,
                'status': 303
            })
        else:
            logs.info('after else-----> ')
            return Response({
                'success': True,
                'redirect_url': redirect_url,
                'status': 200,
                'email': user.email,
                'email_valid': 1
            })
        #return response
        logs.debug("Setting user session to never expire")
    except Exception as exc:  # pylint: disable=broad-except
        # AUDIT_logs.critical("Login failed - Could not create session. Is memcached running?")
        logs.critical(
            "Login failed - Could not create session. Is memcached running?")
        logs.exception(exc)
Esempio n. 32
0
 def assert_enrolled(self):
     """ Asserts that self.ext_user is enrolled in self.course. """
     self.assertTrue(
         CourseEnrollment.is_enrolled(self.ext_user, self.course.id),
         'User ext_user should have been enrolled in the course')
Esempio n. 33
0
def check_course_access(course,
                        user,
                        action,
                        check_if_enrolled=False,
                        check_survey_complete=True):
    """
    Check that the user has the access to perform the specified action
    on the course (CourseDescriptor|CourseOverview).

    check_if_enrolled: If true, additionally verifies that the user is enrolled.
    check_survey_complete: If true, additionally verifies that the user has completed the survey.
    """
    # Allow staff full access to the course even if not enrolled
    if has_access(user, 'staff', course.id):
        return

    request = get_current_request()
    check_content_start_date_for_masquerade_user(course.id, user, request,
                                                 course.start)

    access_response = has_access(user, action, course, course.id)
    if not access_response:
        # Redirect if StartDateError
        if isinstance(access_response, StartDateError):
            start_date = strftime_localized(course.start, 'SHORT_DATE')
            params = QueryDict(mutable=True)
            params['notlive'] = start_date
            raise CourseAccessRedirect(
                '{dashboard_url}?{params}'.format(
                    dashboard_url=reverse('dashboard'),
                    params=params.urlencode()), access_response)

        # Redirect if AuditExpiredError
        if isinstance(access_response, AuditExpiredError):
            params = QueryDict(mutable=True)
            params[
                'access_response_error'] = access_response.additional_context_user_message
            raise CourseAccessRedirect(
                '{dashboard_url}?{params}'.format(
                    dashboard_url=reverse('dashboard'),
                    params=params.urlencode()), access_response)

        # Redirect if the user must answer a survey before entering the course.
        if isinstance(access_response, MilestoneAccessError):
            raise CourseAccessRedirect(
                '{dashboard_url}'.format(dashboard_url=reverse('dashboard'), ),
                access_response)

        # Deliberately return a non-specific error message to avoid
        # leaking info about access control settings
        raise CoursewareAccessException(access_response)

    if check_if_enrolled:
        # If the user is not enrolled, redirect them to the about page
        if not CourseEnrollment.is_enrolled(user, course.id):
            raise CourseAccessRedirect(
                reverse('about_course', args=[six.text_type(course.id)]))

    # Redirect if the user must answer a survey before entering the course.
    if check_survey_complete and action == 'load':
        if is_survey_required_and_unanswered(user, course):
            raise CourseAccessRedirect(
                reverse('course_survey', args=[six.text_type(course.id)]))
Esempio n. 34
0
def user_profile(request, course_key, user_id):
    """
    Renders a response to display the user profile page (shown after clicking
    on a post author's username).
    """
    user = cc.User.from_django_user(request.user)
    course = get_course_with_access(request.user,
                                    'load',
                                    course_key,
                                    check_if_enrolled=True)

    try:
        # If user is not enrolled in the course, do not proceed.
        django_user = User.objects.get(id=user_id)
        if not CourseEnrollment.is_enrolled(django_user, course.id):
            raise Http404

        query_params = {
            'page': request.GET.get('page', 1),
            'per_page':
            THREADS_PER_PAGE,  # more than threads_per_page to show more activities
        }

        try:
            group_id = get_group_id_for_comments_service(request, course_key)
        except ValueError:
            return HttpResponseServerError("Invalid group_id")
        if group_id is not None:
            query_params['group_id'] = group_id
            profiled_user = cc.User(id=user_id,
                                    course_id=course_key,
                                    group_id=group_id)
        else:
            profiled_user = cc.User(id=user_id, course_id=course_key)

        threads, page, num_pages = profiled_user.active_threads(query_params)
        query_params['page'] = page
        query_params['num_pages'] = num_pages

        with newrelic_function_trace("get_metadata_for_threads"):
            user_info = cc.User.from_django_user(request.user).to_dict()
            annotated_content_info = utils.get_metadata_for_threads(
                course_key, threads, request.user, user_info)

        is_staff = has_permission(request.user, 'openclose_thread', course.id)
        threads = [
            utils.prepare_content(thread, course_key, is_staff)
            for thread in threads
        ]
        with newrelic_function_trace("add_courseware_context"):
            add_courseware_context(threads, course, request.user)
        if request.is_ajax():
            return utils.JsonResponse({
                'discussion_data':
                threads,
                'page':
                query_params['page'],
                'num_pages':
                query_params['num_pages'],
                'annotated_content_info':
                annotated_content_info,
            })
        else:
            user_roles = django_user.roles.filter(
                course_id=course.id).order_by("name").values_list(
                    "name", flat=True).distinct()

            with newrelic_function_trace("get_cohort_info"):
                course_discussion_settings = get_course_discussion_settings(
                    course_key)
                user_group_id = get_group_id_for_user(
                    request.user, course_discussion_settings)

            context = _create_base_discussion_view_context(request, course_key)
            context.update({
                'django_user':
                django_user,
                'django_user_roles':
                user_roles,
                'profiled_user':
                profiled_user.to_dict(),
                'threads':
                threads,
                'user_group_id':
                user_group_id,
                'annotated_content_info':
                annotated_content_info,
                'page':
                query_params['page'],
                'num_pages':
                query_params['num_pages'],
                'sort_preference':
                user.default_sort_key,
                'learner_profile_page_url':
                reverse('learner_profile',
                        kwargs={'username': django_user.username}),
            })

            return render_to_response(
                'discussion/discussion_profile_page.html', context)
    except User.DoesNotExist:
        raise Http404
Esempio n. 35
0
def show_enroll_banner(user, course_key):
    # This should be in sync with `student/views/views.py:course_info`
    return (user.is_authenticated()
            and not CourseEnrollment.is_enrolled(user, course_key))
Esempio n. 36
0
 def is_enabled(cls, course, user=None):
     if user is None:
         return True
     return bool(
         CourseEnrollment.is_enrolled(user, course.id)
         or has_access(user, 'staff', course, course.id))
Esempio n. 37
0
def _do_unenroll_students(course_key, students, email_students=False):
    """
    Do the actual work of un-enrolling multiple students, presented as a string
    of emails separated by commas or returns
    `course_key` is id of course (a `str`)
    `students` is string of student emails separated by commas or returns (a `str`)
    `email_students` is user input preference (a `boolean`)
    """

    old_students, __ = get_and_clean_student_list(students)
    status = dict([x, 'unprocessed'] for x in old_students)

    stripped_site_name = microsite.get_value(
        'SITE_NAME',
        settings.SITE_NAME
    )
    if email_students:
        course = modulestore().get_course(course_key)
        # Composition of email
        data = {
            'site_name': stripped_site_name,
            'course': course
        }

    for student in old_students:

        isok = False
        cea = CourseEnrollmentAllowed.objects.filter(course_id=course_key, email=student)
        # Will be 0 or 1 records as there is a unique key on email + course_id
        if cea:
            cea[0].delete()
            status[student] = "un-enrolled"
            isok = True

        try:
            user = User.objects.get(email=student)
        except User.DoesNotExist:

            if isok and email_students:
                # User was allowed to join but had not signed up yet
                data['email_address'] = student
                data['message'] = 'allowed_unenroll'
                send_mail_ret = send_mail_to_student(student, data)
                status[student] += (', email sent' if send_mail_ret else '')

            continue

        # Will be 0 or 1 records as there is a unique key on user + course_id
        if CourseEnrollment.is_enrolled(user, course_key):
            try:
                CourseEnrollment.unenroll(user, course_key)
                status[student] = "un-enrolled"
                if email_students:
                    # User was enrolled
                    data['email_address'] = student
                    data['full_name'] = user.profile.name
                    data['message'] = 'enrolled_unenroll'
                    send_mail_ret = send_mail_to_student(student, data)
                    status[student] += (', email sent' if send_mail_ret else '')

            except Exception:  # pylint: disable=broad-except
                if not isok:
                    status[student] = "Error!  Failed to un-enroll"

    datatable = {'header': ['StudentEmail', 'action']}
    datatable['data'] = [[x, status[x]] for x in sorted(status)]
    datatable['title'] = _('Un-enrollment of students')

    return dict(datatable=datatable)
Esempio n. 38
0
 def _attach_course_run_is_enrolled(self, run_mode):
     run_mode['is_enrolled'] = CourseEnrollment.is_enrolled(
         self.user, self.course_run_key)
Esempio n. 39
0
def create_user_profile_context(request, course_key, user_id):
    """ Generate a context dictionary for the user profile. """
    user = cc.User.from_django_user(request.user)
    course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)

    # If user is not enrolled in the course, do not proceed.
    django_user = User.objects.get(id=user_id)
    if not CourseEnrollment.is_enrolled(django_user, course.id):
        raise Http404

    query_params = {
        'page': request.GET.get('page', 1),
        'per_page': THREADS_PER_PAGE,   # more than threads_per_page to show more activities
    }

    group_id = get_group_id_for_comments_service(request, course_key)
    if group_id is not None:
        query_params['group_id'] = group_id
        profiled_user = cc.User(id=user_id, course_id=course_key, group_id=group_id)
    else:
        profiled_user = cc.User(id=user_id, course_id=course_key)

    threads, page, num_pages = profiled_user.active_threads(query_params)
    query_params['page'] = page
    query_params['num_pages'] = num_pages

    with function_trace("get_metadata_for_threads"):
        user_info = cc.User.from_django_user(request.user).to_dict()
        annotated_content_info = utils.get_metadata_for_threads(course_key, threads, request.user, user_info)

    is_staff = has_permission(request.user, 'openclose_thread', course.id)
    threads = [utils.prepare_content(thread, course_key, is_staff) for thread in threads]
    with function_trace("add_courseware_context"):
        add_courseware_context(threads, course, request.user)

        # TODO: LEARNER-3854: If we actually implement Learner Analytics code, this
        #   code was original protected to not run in user_profile() if is_ajax().
        #   Someone should determine if that is still necessary (i.e. was that ever
        #   called as is_ajax()) and clean this up as necessary.
        user_roles = django_user.roles.filter(
            course_id=course.id
        ).order_by("name").values_list("name", flat=True).distinct()

        with function_trace("get_cohort_info"):
            course_discussion_settings = get_course_discussion_settings(course_key)
            user_group_id = get_group_id_for_user(request.user, course_discussion_settings)

        context = _create_base_discussion_view_context(request, course_key)
        context.update({
            'django_user': django_user,
            'django_user_roles': user_roles,
            'profiled_user': profiled_user.to_dict(),
            'threads': threads,
            'user_group_id': user_group_id,
            'annotated_content_info': annotated_content_info,
            'page': query_params['page'],
            'num_pages': query_params['num_pages'],
            'sort_preference': user.default_sort_key,
            'learner_profile_page_url': reverse('learner_profile', kwargs={'username': django_user.username}),
        })
        return context
Esempio n. 40
0
 def test_purchased_callback(self):
     reg1 = PaidCourseRegistration.add_to_order(self.cart, self.course_id)
     self.cart.purchase()
     self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course_id))
     reg1 = PaidCourseRegistration.objects.get(id=reg1.id)  # reload from DB to get side-effect
     self.assertEqual(reg1.status, "purchased")
Esempio n. 41
0
    def test_enrollment_limit_by_domain(self):
        """
            Tests that the enrollmentDomain setting is properly limiting enrollment to those who have
            the proper external auth
        """

        # create 2 course, one with limited enrollment one without
        shib_course = CourseFactory.create(org='Stanford',
                                           number='123',
                                           display_name='Shib Only')
        shib_course.enrollment_domain = 'shib:https://idp.stanford.edu/'
        self.store.update_item(shib_course, '**replace_user**')

        open_enroll_course = CourseFactory.create(
            org='MITx', number='999', display_name='Robot Super Course')
        open_enroll_course.enrollment_domain = ''
        self.store.update_item(open_enroll_course, '**replace_user**')

        # create 3 kinds of students, external_auth matching shib_course, external_auth not matching, no external auth
        shib_student = UserFactory.create()
        shib_student.save()
        extauth = ExternalAuthMap(
            external_id='*****@*****.**',
            external_email='',
            external_domain='shib:https://idp.stanford.edu/',
            external_credentials="",
            user=shib_student)
        extauth.save()

        other_ext_student = UserFactory.create()
        other_ext_student.username = "******"
        other_ext_student.email = "*****@*****.**"
        other_ext_student.save()
        extauth = ExternalAuthMap(external_id='*****@*****.**',
                                  external_email='',
                                  external_domain='shib:https://other.edu/',
                                  external_credentials="",
                                  user=other_ext_student)
        extauth.save()

        int_student = UserFactory.create()
        int_student.username = "******"
        int_student.email = "*****@*****.**"
        int_student.save()

        # Tests the two case for courses, limited and not
        for course in [shib_course, open_enroll_course]:
            for student in [shib_student, other_ext_student, int_student]:
                request = self.request_factory.post('/change_enrollment')
                request.POST.update({
                    'enrollment_action': 'enroll',
                    'course_id': course.id
                })
                request.user = student
                response = change_enrollment(request)
                # If course is not limited or student has correct shib extauth then enrollment should be allowed
                if course is open_enroll_course or student is shib_student:
                    self.assertEqual(response.status_code, 200)
                    self.assertTrue(
                        CourseEnrollment.is_enrolled(student, course.id))
                    # Clean up
                    CourseEnrollment.unenroll(student, course.id)
                else:
                    self.assertEqual(response.status_code, 400)
                    self.assertFalse(
                        CourseEnrollment.is_enrolled(student, course.id))
Esempio n. 42
0
    def get(self,
            request,
            course_id,
            chapter=None,
            section=None,
            position=None):
        """
        Displays courseware accordion and associated content.  If course, chapter,
        and section are all specified, renders the page, or returns an error if they
        are invalid.

        If section is not specified, displays the accordion opened to the right
        chapter.

        If neither chapter or section are specified, displays the user's most
        recent chapter, or the first chapter if this is the user's first visit.

        Arguments:
            request: HTTP request
            course_id (unicode): course id
            chapter (unicode): chapter url_name
            section (unicode): section url_name
            position (unicode): position in module, eg of <sequential> module
        """
        self.course_key = CourseKey.from_string(course_id)

        if not (request.user.is_authenticated
                or self.enable_unenrolled_access):
            return redirect_to_login(request.get_full_path())

        self.original_chapter_url_name = chapter
        self.original_section_url_name = section
        self.chapter_url_name = chapter
        self.section_url_name = section
        self.position = position
        self.chapter, self.section = None, None
        self.course = None
        self.url = request.path

        try:
            set_custom_metrics_for_course_key(self.course_key)
            self._clean_position()
            with modulestore().bulk_operations(self.course_key):

                self.view = STUDENT_VIEW

                self.course = get_course_with_access(
                    request.user,
                    'load',
                    self.course_key,
                    depth=CONTENT_DEPTH,
                    check_if_enrolled=True,
                    check_if_authenticated=True)
                self.course_overview = CourseOverview.get_from_id(
                    self.course.id)
                self.is_staff = has_access(request.user, 'staff', self.course)

                # There's only one situation where we want to show the public view
                if (not self.is_staff and self.enable_unenrolled_access
                        and self.course.course_visibility
                        == COURSE_VISIBILITY_PUBLIC
                        and not CourseEnrollment.is_enrolled(
                            request.user, self.course_key)):
                    self.view = PUBLIC_VIEW

                self.can_masquerade = request.user.has_perm(
                    MASQUERADE_AS_STUDENT, self.course)
                self._setup_masquerade_for_effective_user()

                return self.render(request)
        except Exception as exception:  # pylint: disable=broad-except
            return CourseTabView.handle_exceptions(request, self.course_key,
                                                   self.course, exception)
Esempio n. 43
0
    def get(self, request, course_id):
        """
        Renders the teams dashboard, which is shown on the "Teams" tab.

        Raises a 404 if the course specified by course_id does not exist, the
        user is not registered for the course, or the teams feature is not enabled.
        """
        course_key = CourseKey.from_string(course_id)
        course = get_course_with_access(request.user, "load", course_key)

        if not is_feature_enabled(course):
            raise Http404

        if not CourseEnrollment.is_enrolled(request.user, course.id) and \
                not has_access(request.user, 'staff', course, course.id):
            raise Http404

        sort_order = 'name'
        topics = get_ordered_topics(course, sort_order)
        topics_page = Paginator(topics, TOPICS_PER_PAGE).page(1)
        topics_serializer = PaginatedTopicSerializer(instance=topics_page,
                                                     context={
                                                         'course_id':
                                                         course.id,
                                                         'sort_order':
                                                         sort_order
                                                     })
        user = request.user

        team_memberships = CourseTeamMembership.get_memberships(
            request.user.username, [course.id])
        team_memberships_page = Paginator(team_memberships,
                                          TEAM_MEMBERSHIPS_PER_PAGE).page(1)
        team_memberships_serializer = PaginatedMembershipSerializer(
            instance=team_memberships_page,
            context={'expand': ('team', )},
        )

        context = {
            "course":
            course,
            "topics":
            topics_serializer.data,
            "user_info": {
                "username": user.username,
                "privileged": has_discussion_privileges(user, course_key),
                "team_memberships_data": team_memberships_serializer.data,
            },
            "topic_url":
            reverse('topics_detail',
                    kwargs={
                        'topic_id': 'topic_id',
                        'course_id': str(course_id)
                    },
                    request=request),
            "topics_url":
            reverse('topics_list', request=request),
            "teams_url":
            reverse('teams_list', request=request),
            "team_memberships_url":
            reverse('team_membership_list', request=request),
            "team_membership_detail_url":
            reverse('team_membership_detail', args=['team_id', user.username]),
            "languages":
            settings.ALL_LANGUAGES,
            "countries":
            list(countries),
            "disable_courseware_js":
            True,
            "teams_base_url":
            reverse('teams_dashboard',
                    request=request,
                    kwargs={'course_id': course_id}),
        }
        return render_to_response("teams/teams.html", context)
Esempio n. 44
0
 def assertUserNotEnrolled(self):
     """ Asserts that the user is NOT enrolled in the course, and that an enrollment event was NOT fired. """
     self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course.id))
     self.assert_no_events_were_emitted()
def regenerate_certificate_for_user(request):
    """
    Regenerate certificates for a user.

    This is meant to be used by support staff through the UI in lms/djangoapps/support

    Arguments:
        request (HttpRequest): The request object

    Returns:
        HttpResponse

    Example Usage:

        POST /certificates/regenerate
            * username: "******"
            * course_key: "edX/DemoX/Demo_Course"

        Response: 200 OK

    """
    # Check the POST parameters, returning a 400 response if they're not valid.
    params, response = _validate_post_params(request.POST)
    if response is not None:
        return response

    # Check that the course exists
    course = modulestore().get_course(params["course_key"])
    if course is None:
        msg = _("The course {course_key} does not exist").format(
            course_key=params["course_key"])
        return HttpResponseBadRequest(msg)

    # Check that the user is enrolled in the course
    if not CourseEnrollment.is_enrolled(params["user"], params["course_key"]):
        msg = _("User {username} is not enrolled in the course {course_key}"
                ).format(username=params["user"].username,
                         course_key=params["course_key"])
        return HttpResponseBadRequest(msg)

    # Attempt to regenerate certificates
    try:
        certificate = api.regenerate_user_certificates(params["user"],
                                                       params["course_key"],
                                                       course=course)
    except:  # pylint: disable=bare-except
        # We are pessimistic about the kinds of errors that might get thrown by the
        # certificates API.  This may be overkill, but we're logging everything so we can
        # track down unexpected errors.
        log.exception(
            "Could not regenerate certificates for user %s in course %s",
            params["user"].id, params["course_key"])
        return HttpResponseServerError(
            _("An unexpected error occurred while regenerating certificates."))

    # Deactivate certificate invalidation by setting active to False.
    _deactivate_invalidation(certificate)

    log.info(
        "Started regenerating certificates for user %s in course %s from the support page.",
        params["user"].id, params["course_key"])
    return HttpResponse(200)
Esempio n. 46
0
def _do_enroll_students(course, course_key, students, secure=False, overload=False, auto_enroll=False, email_students=False, is_shib_course=False):
    """
    Do the actual work of enrolling multiple students, presented as a string
    of emails separated by commas or returns
    `course` is course object
    `course_key` id of course (a CourseKey)
    `students` string of student emails separated by commas or returns (a `str`)
    `overload` un-enrolls all existing students (a `boolean`)
    `auto_enroll` is user input preference (a `boolean`)
    `email_students` is user input preference (a `boolean`)
    """

    new_students, new_students_lc = get_and_clean_student_list(students)
    status = dict([x, 'unprocessed'] for x in new_students)

    if overload:  # delete all but staff
        todelete = CourseEnrollment.objects.filter(course_id=course_key)
        for enrollee in todelete:
            if not has_access(enrollee.user, 'staff', course) and enrollee.user.email.lower() not in new_students_lc:
                status[enrollee.user.email] = 'deleted'
                enrollee.deactivate()
            else:
                status[enrollee.user.email] = 'is staff'
        ceaset = CourseEnrollmentAllowed.objects.filter(course_id=course_key)
        for cea in ceaset:
            status[cea.email] = 'removed from pending enrollment list'
        ceaset.delete()

    if email_students:
        protocol = 'https' if secure else 'http'
        stripped_site_name = microsite.get_value(
            'SITE_NAME',
            settings.SITE_NAME
        )
        # TODO: Use request.build_absolute_uri rather than '{proto}://{site}{path}'.format
        # and check with the Services team that this works well with microsites
        registration_url = '{proto}://{site}{path}'.format(
            proto=protocol,
            site=stripped_site_name,
            path=reverse('register_user')
        )
        course_url = '{proto}://{site}{path}'.format(
            proto=protocol,
            site=stripped_site_name,
            path=reverse('course_root', kwargs={'course_id': course_key.to_deprecated_string()})
        )
        # We can't get the url to the course's About page if the marketing site is enabled.
        course_about_url = None
        if not settings.FEATURES.get('ENABLE_MKTG_SITE', False):
            course_about_url = u'{proto}://{site}{path}'.format(
                proto=protocol,
                site=stripped_site_name,
                path=reverse('about_course', kwargs={'course_id': course_key.to_deprecated_string()})
            )

        # Composition of email
        email_data = {
            'site_name': stripped_site_name,
            'registration_url': registration_url,
            'course': course,
            'auto_enroll': auto_enroll,
            'course_url': course_url,
            'course_about_url': course_about_url,
            'is_shib_course': is_shib_course
        }

    for student in new_students:
        try:
            user = User.objects.get(email=student)
        except User.DoesNotExist:

            # Student not signed up yet, put in pending enrollment allowed table
            cea = CourseEnrollmentAllowed.objects.filter(email=student, course_id=course_key)

            # If enrollmentallowed already exists, update auto_enroll flag to however it was set in UI
            # Will be 0 or 1 records as there is a unique key on email + course_id
            if cea:
                cea[0].auto_enroll = auto_enroll
                cea[0].save()
                status[student] = 'user does not exist, enrollment already allowed, pending with auto enrollment ' \
                    + ('on' if auto_enroll else 'off')
                continue

            # EnrollmentAllowed doesn't exist so create it
            cea = CourseEnrollmentAllowed(email=student, course_id=course_key, auto_enroll=auto_enroll)
            cea.save()

            status[student] = 'user does not exist, enrollment allowed, pending with auto enrollment ' \
                + ('on' if auto_enroll else 'off')

            if email_students:
                # User is allowed to enroll but has not signed up yet
                email_data['email_address'] = student
                email_data['message'] = 'allowed_enroll'
                send_mail_ret = send_mail_to_student(student, email_data)
                status[student] += (', email sent' if send_mail_ret else '')
            continue

        # Student has already registered
        if CourseEnrollment.is_enrolled(user, course_key):
            status[student] = 'already enrolled'
            continue

        try:
            # Not enrolled yet
            CourseEnrollment.enroll(user, course_key)
            status[student] = 'added'

            if email_students:
                # User enrolled for first time, populate dict with user specific info
                email_data['email_address'] = student
                email_data['full_name'] = user.profile.name
                email_data['message'] = 'enrolled_enroll'
                send_mail_ret = send_mail_to_student(student, email_data)
                status[student] += (', email sent' if send_mail_ret else '')

        except Exception:  # pylint: disable=broad-except
            status[student] = 'rejected'

    datatable = {'header': ['StudentEmail', 'action']}
    datatable['data'] = [[x, status[x]] for x in sorted(status)]
    datatable['title'] = _('Enrollment of students')

    def sf(stat):  # pylint: disable=invalid-name
        return [x for x in status if status[x] == stat]

    data = dict(added=sf('added'), rejected=sf('rejected') + sf('exists'),
                deleted=sf('deleted'), datatable=datatable)

    return data
Esempio n. 47
0
    def add_to_order(cls,
                     order,
                     course_id,
                     mode_slug=CourseMode.DEFAULT_MODE_SLUG,
                     cost=None,
                     currency=None):
        """
        A standardized way to create these objects, with sensible defaults filled in.
        Will update the cost if called on an order that already carries the course.

        Returns the order item
        """
        # First a bunch of sanity checks
        try:
            course = course_from_id(
                course_id
            )  # actually fetch the course to make sure it exists, use this to
            # throw errors if it doesn't
        except ItemNotFoundError:
            log.error(
                "User {} tried to add non-existent course {} to cart id {}".
                format(order.user.email, course_id, order.id))
            raise CourseDoesNotExistException

        if cls.contained_in_order(order, course_id):
            log.warning(
                "User {} tried to add PaidCourseRegistration for course {}, already in cart id {}"
                .format(order.user.email, course_id, order.id))
            raise ItemAlreadyInCartException

        if CourseEnrollment.is_enrolled(user=order.user, course_id=course_id):
            log.warning(
                "User {} trying to add course {} to cart id {}, already registered"
                .format(order.user.email, course_id, order.id))
            raise AlreadyEnrolledInCourseException

        ### Validations done, now proceed
        ### handle default arguments for mode_slug, cost, currency
        course_mode = CourseMode.mode_for_course(course_id, mode_slug)
        if not course_mode:
            # user could have specified a mode that's not set, in that case return the DEFAULT_MODE
            course_mode = CourseMode.DEFAULT_MODE
        if not cost:
            cost = course_mode.min_price
        if not currency:
            currency = course_mode.currency

        super(PaidCourseRegistration, cls).add_to_order(order,
                                                        course_id,
                                                        cost,
                                                        currency=currency)

        item, created = cls.objects.get_or_create(order=order,
                                                  user=order.user,
                                                  course_id=course_id)
        item.status = order.status
        item.mode = course_mode.slug
        item.qty = 1
        item.unit_cost = cost
        item.line_desc = 'Registration for Course: {0}'.format(
            course.display_name_with_default)
        item.currency = currency
        order.currency = currency
        item.report_comments = item.csv_report_comments
        order.save()
        item.save()
        log.info(
            "User {} added course registration {} to cart: order {}".format(
                order.user.email, course_id, order.id))
        return item
Esempio n. 48
0
    def process_request(self, request):
        # look to see if the request is prefixed with an asset prefix tag
        if (request.path.startswith('/' + XASSET_LOCATION_TAG + '/')
                or request.path.startswith('/' +
                                           AssetLocator.CANONICAL_NAMESPACE)):
            try:
                loc = StaticContent.get_location_from_path(request.path)
            except (InvalidLocationError, InvalidKeyError):
                # return a 'Bad Request' to browser as we have a malformed Location
                response = HttpResponse()
                response.status_code = 400
                return response

            # first look in our cache so we don't have to round-trip to the DB
            content = get_cached_content(loc)
            if content is None:
                # nope, not in cache, let's fetch from DB
                try:
                    content = AssetManager.find(loc, as_stream=True)
                except NotFoundError:
                    response = HttpResponse()
                    response.status_code = 404
                    return response

                # since we fetched it from DB, let's cache it going forward, but only if it's < 1MB
                # this is because I haven't been able to find a means to stream data out of memcached
                if content.length is not None:
                    if content.length < 1048576:
                        # since we've queried as a stream, let's read in the stream into memory to set in cache
                        content = content.copy_to_in_mem()
                        set_cached_content(content)
            else:
                # NOP here, but we may wish to add a "cache-hit" counter in the future
                pass

            # Check that user has access to content
            if getattr(content, "locked", False):
                if not hasattr(request,
                               "user") or not request.user.is_authenticated():
                    return HttpResponseForbidden('Unauthorized')
                if not request.user.is_staff:
                    if getattr(
                            loc, 'deprecated', False
                    ) and not CourseEnrollment.is_enrolled_by_partial(
                            request.user, loc.course_key):
                        return HttpResponseForbidden('Unauthorized')
                    if not getattr(loc, 'deprecated',
                                   False) and not CourseEnrollment.is_enrolled(
                                       request.user, loc.course_key):
                        return HttpResponseForbidden('Unauthorized')

            # convert over the DB persistent last modified timestamp to a HTTP compatible
            # timestamp, so we can simply compare the strings
            last_modified_at_str = content.last_modified_at.strftime(
                "%a, %d-%b-%Y %H:%M:%S GMT")

            # see if the client has cached this content, if so then compare the
            # timestamps, if they are the same then just return a 304 (Not Modified)
            if 'HTTP_IF_MODIFIED_SINCE' in request.META:
                if_modified_since = request.META['HTTP_IF_MODIFIED_SINCE']
                if if_modified_since == last_modified_at_str:
                    return HttpResponseNotModified()

            # *** File streaming within a byte range ***
            # If a Range is provided, parse Range attribute of the request
            # Add Content-Range in the response if Range is structurally correct
            # Request -> Range attribute structure: "Range: bytes=first-[last]"
            # Response -> Content-Range attribute structure: "Content-Range: bytes first-last/totalLength"
            # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35
            response = None
            if request.META.get('HTTP_RANGE'):
                # Data from cache (StaticContent) has no easy byte management, so we use the DB instead (StaticContentStream)
                if type(content) == StaticContent:
                    content = AssetManager.find(loc, as_stream=True)

                header_value = request.META['HTTP_RANGE']
                try:
                    unit, ranges = parse_range_header(header_value,
                                                      content.length)
                except ValueError as exception:
                    # If the header field is syntactically invalid it should be ignored.
                    log.exception(u"%s in Range header: %s for content: %s",
                                  exception.message, header_value,
                                  unicode(loc))
                else:
                    if unit != 'bytes':
                        # Only accept ranges in bytes
                        log.warning(
                            u"Unknown unit in Range header: %s for content: %s",
                            header_value, unicode(loc))
                    elif len(ranges) > 1:
                        # According to Http/1.1 spec content for multiple ranges should be sent as a multipart message.
                        # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.16
                        # But we send back the full content.
                        log.warning(
                            u"More than 1 ranges in Range header: %s for content: %s",
                            header_value, unicode(loc))
                    else:
                        first, last = ranges[0]

                        if 0 <= first <= last < content.length:
                            # If the byte range is satisfiable
                            response = HttpResponse(
                                content.stream_data_in_range(first, last))
                            response[
                                'Content-Range'] = 'bytes {first}-{last}/{length}'.format(
                                    first=first,
                                    last=last,
                                    length=content.length)
                            response['Content-Length'] = str(last - first + 1)
                            response.status_code = 206  # Partial Content
                        else:
                            log.warning(
                                u"Cannot satisfy ranges in Range header: %s for content: %s",
                                header_value, unicode(loc))
                            return HttpResponse(
                                status=416)  # Requested Range Not Satisfiable

            # If Range header is absent or syntactically invalid return a full content response.
            if response is None:
                response = HttpResponse(content.stream_data())
                response['Content-Length'] = content.length

            # "Accept-Ranges: bytes" tells the user that only "bytes" ranges are allowed
            response['Accept-Ranges'] = 'bytes'
            response['Content-Type'] = content.content_type
            response['Last-Modified'] = last_modified_at_str

            return response
Esempio n. 49
0
    def _validate_and_parse(self, batch_object):
        """
        Performs validation on the batch object to make sure it is in the proper format.

        Parameters:
            * batch_object: The data provided to a POST. The expected format is the following:
            {
                "username": "******",
                "course_key": "course-key",
                "blocks": {
                    "block_key1": 0.0,
                    "block_key2": 1.0,
                    "block_key3": 1.0,
                }
            }


        Return Value:
            * tuple: (User, CourseKey, List of tuples (UsageKey, completion_float)

        Raises:

            django.core.exceptions.ValidationError:
                If any aspect of validation fails a ValidationError is raised.

            ObjectDoesNotExist:
                If a database object cannot be found an ObjectDoesNotExist is raised.
        """
        if not waffle.waffle().is_enabled(waffle.ENABLE_COMPLETION_TRACKING):
            raise ValidationError(
                _("BlockCompletion.objects.submit_batch_completion should not be called when the feature is disabled."
                  ))

        for key in self.REQUIRED_KEYS:
            if key not in batch_object:
                raise ValidationError(
                    _("Key '{key}' not found.".format(key=key)))

        username = batch_object['username']
        user = User.objects.get(username=username)

        course_key = batch_object['course_key']
        try:
            course_key_obj = CourseKey.from_string(course_key)
        except InvalidKeyError:
            raise ValidationError(
                _("Invalid course key: {}").format(course_key))
        course_structure = CourseStructure.objects.get(
            course_id=course_key_obj)

        if not CourseEnrollment.is_enrolled(user, course_key_obj):
            raise ValidationError(_('User is not enrolled in course.'))

        blocks = batch_object['blocks']
        block_objs = []
        for block_key in blocks:
            if block_key not in course_structure.structure['blocks'].keys():
                raise ValidationError(
                    _("Block with key: '{key}' is not in course {course}").
                    format(key=block_key, course=course_key))

            block_key_obj = UsageKey.from_string(block_key)
            completion = float(blocks[block_key])
            block_objs.append((block_key_obj, completion))

        return user, course_key_obj, block_objs
Esempio n. 50
0
    def test_shib_login_enrollment(self):
        """
            A functionality test that a student with an existing shib login
            can auto-enroll in a class with GET or POST params.  Also tests the direction functionality of
            the 'next' GET/POST param
        """
        student = UserFactory.create()
        extauth = ExternalAuthMap(
            external_id='*****@*****.**',
            external_email='',
            external_domain='shib:https://idp.stanford.edu/',
            external_credentials="",
            internal_password="******",
            user=student)
        student.set_password("password")
        student.save()
        extauth.save()

        course = CourseFactory.create(org='Stanford',
                                      number='123',
                                      display_name='Shib Only')
        course.enrollment_domain = 'shib:https://idp.stanford.edu/'
        metadata = own_metadata(course)
        metadata['enrollment_domain'] = course.enrollment_domain
        self.store.update_metadata(course.location.url(), metadata)

        # use django test client for sessions and url processing
        # no enrollment before trying
        self.assertFalse(CourseEnrollment.is_enrolled(student, course.id))
        self.client.logout()
        request_kwargs = {
            'path': '/shib-login/',
            'data': {
                'enrollment_action': 'enroll',
                'course_id': course.id,
                'next': '/testredirect'
            },
            'follow': False,
            'REMOTE_USER': '******',
            'Shib-Identity-Provider': 'https://idp.stanford.edu/'
        }
        response = self.client.get(**request_kwargs)
        # successful login is a redirect to "/"
        self.assertEqual(response.status_code, 302)
        self.assertEqual(response['location'],
                         'http://testserver/testredirect')
        # now there is enrollment
        self.assertTrue(CourseEnrollment.is_enrolled(student, course.id))

        # Clean up and try again with POST (doesn't happen with real production shib, doing this for test coverage)
        self.client.logout()
        CourseEnrollment.unenroll(student, course.id)
        self.assertFalse(CourseEnrollment.is_enrolled(student, course.id))

        response = self.client.post(**request_kwargs)
        # successful login is a redirect to "/"
        self.assertEqual(response.status_code, 302)
        self.assertEqual(response['location'],
                         'http://testserver/testredirect')
        # now there is enrollment
        self.assertTrue(CourseEnrollment.is_enrolled(student, course.id))
Esempio n. 51
0
    def _validate_data(self, request, errors, new):
        """
        Validate imported data.
        :param new: if `True`, new user will be created and enrolled, otherwise existing user will be enrolled
        :returns `request.data` copy with added `course`, `course_key` and ('company` if `new` else `user_object`)
        """
        validated_data = request.data.copy()
        internal = validated_data.get('internal', False)
        statuses = validated_data.get('statuses', [])
        company_id = validated_data.get('company_id', '')
        course_id = validated_data.get('course_id', '')
        status = validated_data.get('status', '').lower()
        user = validated_data.get('user', {})
        username, email = user.get('username', ''), user.get('email', '')

        # Check for empty fields.
        for key, value in user.items():
            if not isinstance(value, bool) and value.strip() == '':
                if key != 'username':
                    self._add_error(errors,
                                    _("Empty field: {}").format(key),
                                    _("Processing Participant"), email)

        # Ensure valid status.
        if status not in statuses:
            self._add_error(errors,
                            _("Status '{}' doesn't exist").format(status),
                            _('Enrolling Participant in Course'), email)

        if new:
            # Ensure email/username integrity.
            if User.objects.filter(Q(email=email)
                                   | Q(username=username)).exists():
                self._add_error(
                    errors,
                    _('Email "{}" or username "{}" already exists').format(
                        email, username), _('Registering Participant'), email)

            # Check that the company exists.
            try:
                validated_data['company'] = Organization.objects.get(
                    id=company_id)
            except Organization.DoesNotExist:
                self._add_error(
                    errors,
                    _("Company {} doesn't exist").format(company_id),
                    _('Enrolling Participant in Company'), email)
        else:
            # Ensure user with provided email exists.
            try:
                validated_data['user_object'] = User.objects.get(email=email)
            except User.DoesNotExist:
                self._add_error(
                    errors,
                    _('User with email "{}" does not exist').format(email),
                    _('Retrieving existing Participant'), email)

            # Check if course is already enrolled.
            if validated_data.get('user_object',
                                  None) and CourseEnrollment.is_enrolled(
                                      validated_data.get('user_object'),
                                      CourseKey.from_string(course_id)):
                self._add_error(
                    errors,
                    _('User with email "{}" is already enrolled').format(
                        email), _("Enrolling Participant in Course"), email)

        # Check that the course exists.
        validated_data['course'], validated_data[
            'course_key'], __ = get_course(request, user, course_id)
        if not validated_data['course']:
            self._add_error(errors,
                            _("Course {} doesn't exist").format(course_id),
                            _('Enrolling Participant in Course'), email)

        # Check if course is internal (if required).
        if internal and not CourseGroupRelationship.objects.filter(
                course_id=course_id, group__type="tag:internal").exists():
            self._add_error(errors,
                            _("Course {} is not Internal").format(course_id),
                            _('Enrolling Participant in Course'), email)

        return validated_data
Esempio n. 52
0
    def render_to_fragment(self, request, course_id=None, **kwargs):
        """
        Renders the course's home page as a fragment.
        """
        course_key = CourseKey.from_string(course_id)
        course = get_course_with_access(request.user, 'load', course_key)

        # Render the course dates as a fragment
        dates_fragment = CourseDatesFragmentView().render_to_fragment(request, course_id=course_id, **kwargs)

        # Render the full content to enrolled users, as well as to course and global staff.
        # Unenrolled users who are not course or global staff are given only a subset.
        user_access = {
            'is_anonymous': request.user.is_anonymous(),
            'is_enrolled': CourseEnrollment.is_enrolled(request.user, course_key),
            'is_staff': has_access(request.user, 'staff', course_key),
        }
        if user_access['is_enrolled'] or user_access['is_staff']:
            outline_fragment = CourseOutlineFragmentView().render_to_fragment(request, course_id=course_id, **kwargs)
            if LATEST_UPDATE_FLAG.is_enabled(course_key):
                update_message_fragment = LatestUpdateFragmentView().render_to_fragment(
                    request, course_id=course_id, **kwargs
                )
            else:
                update_message_fragment = WelcomeMessageFragmentView().render_to_fragment(
                    request, course_id=course_id, **kwargs
                )
            course_sock_fragment = CourseSockFragmentView().render_to_fragment(request, course=course, **kwargs)
            has_visited_course, resume_course_url = self._get_resume_course_info(request, course_id)
        else:
            # Redirect the user to the dashboard if they are not enrolled and
            # this is a course that does not support direct enrollment.
            if not can_self_enroll_in_course(course_key):
                raise CourseAccessRedirect(reverse('dashboard'))

            # Set all the fragments
            outline_fragment = None
            update_message_fragment = None
            course_sock_fragment = None
            has_visited_course = None
            resume_course_url = None

        # Get the handouts
        handouts_html = self._get_course_handouts(request, course)

        # Get the course tools enabled for this user and course
        course_tools = CourseToolsPluginManager.get_enabled_course_tools(request, course_key)

        # Grab the course home messages fragment to render any relevant django messages
        course_home_message_fragment = CourseHomeMessageFragmentView().render_to_fragment(
            request, course_id=course_id, user_access=user_access, **kwargs
        )

        # Render the course home fragment
        context = {
            'request': request,
            'csrf': csrf(request)['csrf_token'],
            'course': course,
            'course_key': course_key,
            'outline_fragment': outline_fragment,
            'handouts_html': handouts_html,
            'course_home_message_fragment': course_home_message_fragment,
            'has_visited_course': has_visited_course,
            'resume_course_url': resume_course_url,
            'course_tools': course_tools,
            'dates_fragment': dates_fragment,
            'update_message_fragment': update_message_fragment,
            'course_sock_fragment': course_sock_fragment,
            'disable_courseware_js': True,
            'uses_pattern_library': True,
        }
        html = render_to_string('course_experience/course-home-fragment.html', context)
        return Fragment(html)
Esempio n. 53
0
def bulk_beta_modify_access(request, course_id):
    """
    Enroll or unenroll users in beta testing program.

    Query parameters:
    - identifiers is string containing a list of emails and/or usernames separated by
      anything split_input_list can handle.
    - action is one of ['add', 'remove']
    """
    course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    action = request.GET.get('action')
    identifiers_raw = request.GET.get('identifiers')
    identifiers = _split_input_list(identifiers_raw)
    email_students = request.GET.get('email_students') in ['true', 'True', True]
    auto_enroll = request.GET.get('auto_enroll') in ['true', 'True', True]
    results = []
    rolename = 'beta'
    course = get_course_by_id(course_id)

    email_params = {}
    if email_students:
        secure = request.is_secure()
        email_params = get_email_params(course, auto_enroll=auto_enroll, secure=secure)

    for identifier in identifiers:
        try:
            error = False
            user_does_not_exist = False
            user = get_student_from_identifier(identifier)

            if action == 'add':
                allow_access(course, user, rolename)
            elif action == 'remove':
                revoke_access(course, user, rolename)
            else:
                return HttpResponseBadRequest(strip_tags(
                    "Unrecognized action '{}'".format(action)
                ))
        except User.DoesNotExist:
            error = True
            user_does_not_exist = True
        # catch and log any unexpected exceptions
        # so that one error doesn't cause a 500.
        except Exception as exc:  # pylint: disable=broad-except
            log.exception("Error while #{}ing student")
            log.exception(exc)
            error = True
        else:
            # If no exception thrown, see if we should send an email
            if email_students:
                send_beta_role_email(action, user, email_params)
            # See if we should autoenroll the student
            if auto_enroll:
                # Check if student is already enrolled
                if not CourseEnrollment.is_enrolled(user, course_id):
                    CourseEnrollment.enroll(user, course_id)

        finally:
            # Tabulate the action result of this email address
            results.append({
                'identifier': identifier,
                'error': error,
                'userDoesNotExist': user_does_not_exist
            })

    response_payload = {
        'action': action,
        'results': results,
    }
    return JsonResponse(response_payload)
Esempio n. 54
0
    def get(self, request, course_id):
        """
        Renders the teams dashboard, which is shown on the "Teams" tab.

        Raises a 404 if the course specified by course_id does not exist, the
        user is not registered for the course, or the teams feature is not enabled.
        """
        course_key = CourseKey.from_string(course_id)
        course = get_course_with_access(request.user, "load", course_key)

        if not is_feature_enabled(course):
            raise Http404

        if not CourseEnrollment.is_enrolled(request.user, course.id) and \
                not has_access(request.user, 'staff', course, course.id):
            raise Http404

        # Even though sorting is done outside of the serializer, sort_order needs to be passed
        # to the serializer so that the paginated results indicate how they were sorted.
        sort_order = 'name'
        topics = get_alphabetical_topics(course)
        topics_page = Paginator(topics, TOPICS_PER_PAGE).page(1)
        # BulkTeamCountPaginatedTopicSerializer will add team counts to the topics in a single
        # bulk operation per page.
        topics_serializer = BulkTeamCountPaginatedTopicSerializer(
            instance=topics_page,
            context={
                'course_id': course.id,
                'sort_order': sort_order
            })
        user = request.user

        team_memberships = CourseTeamMembership.get_memberships(
            request.user.username, [course.id])
        team_memberships_page = Paginator(team_memberships,
                                          TEAM_MEMBERSHIPS_PER_PAGE).page(1)
        team_memberships_serializer = PaginatedMembershipSerializer(
            instance=team_memberships_page,
            context={'expand': ('team', )},
        )

        context = {
            "course":
            course,
            "topics":
            topics_serializer.data,
            # It is necessary to pass both privileged and staff because only privileged users can
            # administer discussion threads, but both privileged and staff users are allowed to create
            # multiple teams (since they are not automatically added to teams upon creation).
            "user_info": {
                "username": user.username,
                "privileged": has_discussion_privileges(user, course_key),
                "staff": bool(has_access(user, 'staff', course_key)),
                "team_memberships_data": team_memberships_serializer.data,
            },
            "topic_url":
            reverse('topics_detail',
                    kwargs={
                        'topic_id': 'topic_id',
                        'course_id': str(course_id)
                    },
                    request=request),
            "topics_url":
            reverse('topics_list', request=request),
            "teams_url":
            reverse('teams_list', request=request),
            "teams_detail_url":
            reverse('teams_detail', args=['team_id']),
            "team_memberships_url":
            reverse('team_membership_list', request=request),
            "team_membership_detail_url":
            reverse('team_membership_detail', args=['team_id', user.username]),
            "languages":
            [[lang[0], _(lang[1])] for lang in settings.ALL_LANGUAGES],  # pylint: disable=translation-of-non-string
            "countries":
            list(countries),
            "disable_courseware_js":
            True,
            "teams_base_url":
            reverse('teams_dashboard',
                    request=request,
                    kwargs={'course_id': course_id}),
        }
        return render_to_response("teams/teams.html", context)
Esempio n. 55
0
 def assert_user_enrollment_occurred(self, course_key):
     """
     Helper method to assert that the user is enrolled in the given course.
     """
     self.assertTrue(CourseEnrollment.is_enrolled(self.user, CourseKey.from_string(course_key)))
Esempio n. 56
0
 def assert_not_enrolled(self):
     """ Asserts that self.ext_user is not enrolled in self.course. """
     self.assertFalse(
         CourseEnrollment.is_enrolled(self.ext_user, self.course.id),
         'Did not expect ext_user to be enrolled in course')
Esempio n. 57
0
 def is_enabled(cls, course, user=None):
     return user and user.is_authenticated and \
         bool(CourseEnrollment.is_enrolled(user, course.id) or has_access(user, 'staff', course, course.id))
Esempio n. 58
0
def mobile_change_enrollment(request):
    """
    Modify the enrollment status for the logged-in user.

    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 or
    as a post-login/registration helper, so the error messages in the responses
    should never actually be user-visible.
    """
    user = request.user

    action = request.POST.get("enrollment_action")
    course_id = request.POST.get("course_id")
    if course_id is None:
        return HttpResponseBadRequest(_("Course id not specified"))

    if not user.is_authenticated():
        return HttpResponseForbidden()

    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
        try:
            course = course_from_id(course_id)
        except ItemNotFoundError:
            log.warning("User {0} tried to enroll in non-existent course {1}"
                        .format(user.username, course_id))
            return HttpResponseBadRequest(_("Course id is invalid"))

        if not has_access(user, course, 'enroll'):
            return HttpResponseBadRequest(_("Enrollment is closed"))

        # see if we have already filled up all allowed enrollments
        is_course_full = CourseEnrollment.is_course_full(course)

        if is_course_full:
            return HttpResponseBadRequest(_("Course is full"))

        # If this course is available in multiple modes, redirect them to a page
        # where they can choose which mode they want.
        available_modes = CourseMode.modes_for_course(course_id)
        if len(available_modes) > 1:
            return HttpResponse(
                reverse("course_modes_choose", kwargs={'course_id': course_id})
            )

        current_mode = available_modes[0]

        course_id_dict = Location.parse_course_id(course_id)
        dog_stats_api.increment(
            "common.student.enrollment",
            tags=[u"org:{org}".format(**course_id_dict),
                  u"course:{course}".format(**course_id_dict),
                  u"run:{name}".format(**course_id_dict)]
        )

        CourseEnrollment.enroll(user, course.id, mode=current_mode.slug)

        return HttpResponse('about')

    elif action == "add_to_cart":
        # Pass the request handling to shoppingcart.views
        # The view in shoppingcart.views performs error handling and logs different errors.  But this elif clause
        # is only used in the "auto-add after user reg/login" case, i.e. it's always wrapped in try_change_enrollment.
        # This means there's no good way to display error messages to the user.  So we log the errors and send
        # the user to the shopping cart page always, where they can reasonably discern the status of their cart,
        # whether things got added, etc

        shoppingcart.views.add_course_to_cart(request, course_id)
        return HttpResponse(
            reverse("shoppingcart.views.show_cart")
        )

    elif action == "unenroll":
        if not CourseEnrollment.is_enrolled(user, course_id):
            return HttpResponseBadRequest(_("You are not enrolled in this course"))
        CourseEnrollment.unenroll(user, course_id)
        course_id_dict = Location.parse_course_id(course_id)
        dog_stats_api.increment(
            "common.student.unenrollment",
            tags=[u"org:{org}".format(**course_id_dict),
                  u"course:{course}".format(**course_id_dict),
                  u"run:{name}".format(**course_id_dict)]
        )
        return HttpResponse()
    else:
        return HttpResponseBadRequest(_("Enrollment action is invalid"))
Esempio n. 59
0
 def test_no_course_id_skips_enroll(self):
     strategy = self._fake_strategy()
     result = pipeline.change_enrollment(strategy, 1, user=self.user)  # pylint: disable=E1111,E1124
     self.assertEqual(result, {})
     self.assertFalse(
         CourseEnrollment.is_enrolled(self.user, self.course.id))
Esempio n. 60
0
def docvid_auth(request, user,courseid):
    # _track_user_login(user, request)
    try:
        #request.session.set_expiry(604800)
        #django_login(request, user, backend='django.contrib.auth.backends.ModelBackend')
        #request.session.set_expiry(604800)
        request.user = user
        user_extra_data = userdetails(user.id)
        displayname = getuserfullprofile(user.id)
        course_id = request.data.get('course_id','')
        course_id = course_id.replace(' ','+')
        #logs.info('course_id %s',courseid)
        course_key = None
        if course_id:
            course_key = CourseKey.from_string(course_id)
            if not CourseEnrollment.is_enrolled(user, course_key) :
               #logs.info("course enrollment function will be call")
                #CourseEnrollment.enroll(user=user, course_key=course_key,mode='honor')
                user_course_enrollment = CourseEnrollment.enroll(user=user, course_key=course_key,mode='honor')
                if user_course_enrollment:
                    #logs.info("course enrollment function will be call")
                    query = course_id.replace('+','%2B')
                    query = query.replace(':','%3A')
                    now_asia = courseid.start.astimezone(timezone('Asia/Kolkata'))
                    now_asia = now_asia.strftime("%I:%M %p")
                    docvidya_course_detail = requests.get('https://drlprdoce-drreddys.cec.ocp.oraclecloud.com//content/published/api/v1.1/items?fields=slug&orderBy=updateddate:desc&limit=1&q=((type eq "DocV-Courses") and (fields.course_id eq "' + query + '"))&channelToken=cf84462f73e542e7a8d270dfd2580730')
                    response = json.loads(docvidya_course_detail.text.encode('utf8'))
                    courseurl = 'www.docvidya.com/docv-courses-detail/'+response['items'][0]['slug']
                    courseurl = courseurl.replace('https://','')
                    #logs.info("docvidya_course_detail--> %s",response['items'][0]['slug'])
                    enrolldata = CourseEnrollment.objects.get(user=user,course_id=course_key)
                    course_image_url = courseid.course_image_url.replace('+','%2B')
                    if '&' in courseid.display_name:
                        course_name = courseid.display_name.replace('&','and')
                    else:
                        course_name = courseid.display_name
                    course_exinfo = course_extrainfo.objects.get(course_id=course_id)
                    #logs.info("course_exinfo--> %s",course_exinfo.__dict__)
                    if course_exinfo.credit_point > 0:
                        points = course_exinfo.credit_point
                    else:
                        points = 0
                    #logs.info("points-->%s",points)
                    #logs.info("api-details--> https://s394336720.t.eloqua.com/e/f2?elqFormName=CourseEvents&elqSiteID=394336720&emailAddress="+user.email+"&CourseId="+courseid.display_number_with_default+"&CourseName="+courseid.display_name+"&BannerURL=https://learn.docmode.org"+str(course_image_url)+"&Status=Enrolled&FullName="+displayname.name+"&DisplayDate="+str(courseid.start.date())+' '+str(now_asia.strftime("%H:%M"))+"&courseURL="+courseurl+"&EnrollmentID="+str(enrolldata.id)+"&points="+str(points)+"&Key=62f74068-90d2-4fd2-bd76-d657fbbbca14&Date="+str(datetime.today()))
                    syncmail = requests.post("https://s394336720.t.eloqua.com/e/f2?elqFormName=CourseEvents&elqSiteID=394336720&emailAddress="+user.email+"&CourseId="+courseid.display_number_with_default+"&CourseName="+course_name+"&BannerURL=https://learn.docmode.org"+str(course_image_url)+"&Status=Enrolled&FullName="+displayname.name+"&DisplayDate="+str(courseid.start.date())+'&DisplayTime='+str(now_asia)+"&courseURL="+courseurl+"&EnrollmentID="+str(enrolldata.id)+"&points="+str(points)+"&Key=62f74068-90d2-4fd2-bd76-d657fbbbca14&CourseDate="+str(courseid.start.strftime("%Y-%m-%dT%H:%M:%SZ")))
                    #logs.info("syncmail--> %s",syncmail.__dict__)

        redirect_url = 'https://docvidya.learn.docmode.org/courses/'+course_id+'/courseware'
        #logs.debug("Setting user session to never expire")
        return Response({
            'success': True,
            'redirect_url':redirect_url,
            'status': 200,
            'email':user.email
        })
        #return response
        #logs.debug("Setting user session to never expire")
    except Exception as exc:  # pylint: disable=broad-except
        # AUDIT_LOG.critical("Login failed - Could not create session. Is memcached running?")
        logs.critical("Login failed - Could not create session. Is memcached running?")
        logs.exception(exc)