def test_enroll_pro_unknown_fail(mocker, user): """ Tests that enroll_in_edx_course_runs raises an UnknownEdxApiEnrollException if an unexpected exception is encountered """ mock_client = mocker.MagicMock() mock_client.enrollments.create_student_enrollment = mocker.Mock( side_effect=ValueError("Unexpected error")) mocker.patch("courseware.api.get_edx_api_client", return_value=mock_client) course_run = CourseRunFactory.build() with pytest.raises(UnknownEdxApiEnrollException): enroll_in_edx_course_runs(user, [course_run])
def test_enroll_pro_api_fail(mocker, user): """ Tests that enroll_in_edx_course_runs raises an EdxApiEnrollErrorException if the request fails for some reason besides an enrollment mode error """ mock_client = mocker.MagicMock() pro_enrollment_response = MockResponse({"message": "no dice"}, status_code=401) mock_client.enrollments.create_student_enrollment = mocker.Mock( side_effect=HTTPError(response=pro_enrollment_response)) mocker.patch("courseware.api.get_edx_api_client", return_value=mock_client) course_run = CourseRunFactory.build() with pytest.raises(EdxApiEnrollErrorException): enroll_in_edx_course_runs(user, [course_run])
def enroll_in_edx(self, user, course_runs): """ Try to perform edX enrollment, but print a message and continue if it fails Args: user (User): The user to enroll course_runs (iterable of CourseRun): The course runs to enroll in :return boolean either the enrollment in edx succeeded or not. """ try: enroll_in_edx_course_runs(user, course_runs) return True except ( EdxApiEnrollErrorException, UnknownEdxApiEnrollException, NoEdxApiAuthError, ) as exc: self.stdout.write(self.style.WARNING(str(exc))) return False
def test_enroll_in_edx_course_runs(mocker, user): """Tests that enroll_in_edx_course_runs uses the EdxApi client to enroll in course runs""" mock_client = mocker.MagicMock() enroll_return_values = ["result1", "result2"] mock_client.enrollments.create_student_enrollment = mocker.Mock( side_effect=enroll_return_values) mocker.patch("courseware.api.get_edx_api_client", return_value=mock_client) course_runs = CourseRunFactory.build_batch(2) enroll_results = enroll_in_edx_course_runs(user, course_runs) mock_client.enrollments.create_student_enrollment.assert_any_call( course_runs[0].courseware_id, mode=EDX_ENROLLMENT_PRO_MODE) mock_client.enrollments.create_student_enrollment.assert_any_call( course_runs[1].courseware_id, mode=EDX_ENROLLMENT_PRO_MODE) assert enroll_results == enroll_return_values
def handle(self, *args, **options): """Run the command""" enrollment_filter = {} if not options["force"]: enrollment_filter["edx_enrolled"] = False if options["run"]: enrollment_filter["run__courseware_id"] = options["run"] if options["uservalues"]: enrollment_filter["user__in"] = fetch_users(options["uservalues"]) course_run_enrollments = CourseRunEnrollment.objects.filter( **enrollment_filter) if course_run_enrollments.count() == 0: self.stderr.write( self.style.ERROR( "No course run enrollments found that match the given filters ({}).\nExiting..." .format(enrollment_filter))) return for enrollment in course_run_enrollments: user = enrollment.user course_run = enrollment.run try: enroll_in_edx_course_runs(user, [course_run]) except Exception as exc: # pylint: disable=broad-except self.stderr.write(self.style.ERROR(str(exc))) else: enrollment.edx_enrolled = True enrollment.save_and_log(None) self.stdout.write( self.style.SUCCESS( "Successfully enrolled user {} ({}) in course run '{}'" .format(user.username, user.email, course_run.courseware_id))) self.stdout.write(self.style.SUCCESS("Done"))
def test_enroll_in_edx_course_runs_audit(mocker, user, error_text): """Tests that enroll_in_edx_course_runs fails over to attempting enrollment with 'audit' mode""" mock_client = mocker.MagicMock() pro_enrollment_response = MockResponse({"message": error_text}) audit_result = {"good": "result"} mock_client.enrollments.create_student_enrollment = mocker.Mock( side_effect=[ HTTPError(response=pro_enrollment_response), audit_result ]) patched_log_error = mocker.patch("courseware.api.log.error") mocker.patch("courseware.api.get_edx_api_client", return_value=mock_client) course_run = CourseRunFactory.build() results = enroll_in_edx_course_runs(user, [course_run]) assert mock_client.enrollments.create_student_enrollment.call_count == 2 mock_client.enrollments.create_student_enrollment.assert_any_call( course_run.courseware_id, mode=EDX_ENROLLMENT_PRO_MODE) mock_client.enrollments.create_student_enrollment.assert_any_call( course_run.courseware_id, mode=EDX_ENROLLMENT_AUDIT_MODE) assert results == [audit_result] patched_log_error.assert_called_once()
def create_run_enrollments(user, runs, keep_failed_enrollments=False, order=None, company=None): """ Creates local records of a user's enrollment in course runs, and attempts to enroll them in edX via API Args: user (User): The user to enroll runs (iterable of CourseRun): The course runs to enroll in order (ecommerce.models.Order or None): The order associated with these enrollments company (ecommerce.models.Company or None): The company on whose behalf these enrollments are being created keep_failed_enrollments: (boolean): If True, keeps the local enrollment record in the database even if the enrollment fails in edX. Returns: (list of CourseRunEnrollment, bool): A list of enrollment objects that were successfully created, paired with a boolean indicating whether or not edX enrollment was successful for all of the given course runs """ successful_enrollments = [] try: enroll_in_edx_course_runs(user, runs) except ( EdxApiEnrollErrorException, UnknownEdxApiEnrollException, NoEdxApiAuthError, HTTPError, RequestsConnectionError, ): log.exception( "edX enrollment failure for user: %s, runs: %s (order: %s)", user, [run.courseware_id for run in runs], order.id if order else None, ) edx_request_success = False if not keep_failed_enrollments: return successful_enrollments, edx_request_success else: edx_request_success = True for run in runs: try: enrollment, created = CourseRunEnrollment.all_objects.get_or_create( user=user, run=run, order=order, defaults=dict(company=company, edx_enrolled=edx_request_success), ) if not created and not enrollment.active: enrollment.edx_enrolled = edx_request_success enrollment.reactivate_and_save() except: # pylint: disable=bare-except mail_api.send_enrollment_failure_message(order, run, details=format_exc()) log.exception( "Failed to create/update enrollment record (user: %s, run: %s, order: %s)", user, run.courseware_id, order.id if order else None, ) else: successful_enrollments.append(enrollment) if enrollment.edx_enrolled: mail_api.send_course_run_enrollment_email(enrollment) return successful_enrollments, edx_request_success