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)
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)
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
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)
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
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)
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))
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))
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))
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))
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))
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))
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))
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))
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))
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))
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)
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
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)
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)
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)
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)
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)
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
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)
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)
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)
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')
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)]))
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
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))
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))
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)
def _attach_course_run_is_enrolled(self, run_mode): run_mode['is_enrolled'] = CourseEnrollment.is_enrolled( self.user, self.course_run_key)
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
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")
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))
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)
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)
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)
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
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
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
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
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))
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
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)
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)
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)
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)))
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')
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))
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"))
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))
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)