def get_base_details(self, enterprise_uuid, course_id): """ Retrieve fundamental details used by both POST and GET versions of this view. Specifically, take an EnterpriseCustomer UUID and a course ID, and transform those into an actual EnterpriseCustomer, a set of details about the course, and a list of the available course modes for that course. """ try: client = CourseApiClient() course_details = client.get_course_details(course_id) except HttpClientError: logger.error('Failed to get course details for course ID: %s', course_id) raise Http404 if course_details is None: logger.error('Unable to find course details for course ID: %s', course_id) raise Http404 enterprise_customer = get_enterprise_customer_or_404(enterprise_uuid) try: enrollment_client = EnrollmentApiClient() modes = enrollment_client.get_course_modes(course_id) except HttpClientError: logger.error( 'Failed to determine available course modes for course ID: %s', course_id) raise Http404 course_modes = [] audit_modes = getattr(settings, 'ENTERPRISE_COURSE_ENROLLMENT_AUDIT_MODES', ['audit', 'honor']) for mode in modes: if mode['min_price']: price_text = '${}'.format(mode['min_price']) else: price_text = self.context_data['free_price_text'] if mode['slug'] in audit_modes: description = self.context_data['audit_text'] else: description = self.context_data['verified_text'] course_modes.append({ 'mode': mode['slug'], 'min_price': mode['min_price'], 'sku': mode['sku'], 'title': mode['name'], 'original_price': price_text, 'final_price': price_text, 'description': description, 'premium': mode['slug'] not in audit_modes }) return enterprise_customer, course_details, course_modes
def clean_course(self): """ Verify course ID and retrieve course details. """ course_id = self.cleaned_data[self.Fields.COURSE].strip() if not course_id: return None try: client = EnrollmentApiClient() return client.get_course_details(course_id) except (HttpClientError, HttpServerError): raise ValidationError(ValidationMessages.INVALID_COURSE_ID.format(course_id=course_id))
def get(self, request, enterprise_uuid, course_id): """ Show course track selection page for the enterprise. Based on `enterprise_uuid` in URL, the view will decide which enterprise customer's course enrollment page is to use. Unauthenticated learners will be redirected to enterprise-linked SSO. A 404 will be raised if any of the following conditions are met: * No enterprise customer uuid kwarg `enterprise_uuid` in request. * No enterprise customer found against the enterprise customer uuid `enterprise_uuid` in the request kwargs. * No course is found in database against the provided `course_id`. """ # Verify that all necessary resources are present verify_edx_resources() enterprise_customer, course, modes = self.get_base_details( enterprise_uuid, course_id) # Create a link between the user and the enterprise customer if it # does not already exist. EnterpriseCustomerUser.objects.get_or_create( enterprise_customer=enterprise_customer, user_id=request.user.id) enrollment_client = EnrollmentApiClient() enrolled_course = enrollment_client.get_course_enrollment( request.user.username, course_id) if (enrolled_course is not None and EnterpriseCourseEnrollment.objects.filter( enterprise_customer_user__enterprise_customer= enterprise_customer, enterprise_customer_user__user_id=request.user.id, course_id=course_id).exists()): # The user is already enrolled in the course through the Enterprise Customer, so redirect to the course # info page. return redirect(LMS_COURSE_URL.format(course_id=course_id)) return self.get_enterprise_course_enrollment_page( request, enterprise_customer, course, modes)
def get_enrolled_course_string(self, enterprise_customer_user): """ Get an HTML string representing the courses the user is enrolled in. """ enrollment_client = EnrollmentApiClient() enrolled_courses = enrollment_client.get_enrolled_courses(self.username(enterprise_customer_user)) course_details = [] courses_client = CourseApiClient() for course in enrolled_courses: course_id = course['course_details']['course_id'] name = courses_client.get_course_details(course_id)['name'] course_details.append({'course_id': course_id, 'course_name': name}) template = '<a href="{url}">{course_name}</a>' joiner = '<br/>' return joiner.join( template.format( url=reverse('about_course', args=[course['course_id']]), course_name=course['course_name'], ) for course in course_details )
def enroll_user(cls, enterprise_customer, user, course_mode, *course_ids): """ Enroll a single user in any number of courses using a particular course mode. Args: enterprise_customer: The EnterpriseCustomer which is sponsoring the enrollment user: The user who needs to be enrolled in the course course_mode: The mode with which the enrollment should be created *course_ids: An iterable containing any number of course IDs to eventually enroll the user in. Returns: Boolean: Whether or not enrollment succeeded for all courses specified """ enterprise_customer_user, __ = EnterpriseCustomerUser.objects.get_or_create( enterprise_customer=enterprise_customer, user_id=user.id ) enrollment_client = EnrollmentApiClient() succeeded = True for course_id in course_ids: try: enrollment_client.enroll_user_in_course(user.username, course_id, course_mode) except HttpClientError as exc: succeeded = False default_message = 'No error message provided' try: error_message = json.loads(exc.content.decode()).get('message', default_message) except ValueError: error_message = default_message logging.error( 'Error while enrolling user %(user)s: %(message)s', dict(user=user.username, message=error_message) ) else: EnterpriseCourseEnrollment.objects.get_or_create( enterprise_customer_user=enterprise_customer_user, course_id=course_id ) return succeeded
def post(self, request, enterprise_uuid, course_id): """ Process a submitted track selection form for the enterprise. """ enterprise_customer, course, course_modes = self.get_base_details( enterprise_uuid, course_id) # Create a link between the user and the enterprise customer if it # does not already exist. enterprise_customer_user, __ = EnterpriseCustomerUser.objects.get_or_create( enterprise_customer=enterprise_customer, user_id=request.user.id) selected_course_mode_name = request.POST.get('course_mode') selected_course_mode = None for course_mode in course_modes: if course_mode['mode'] == selected_course_mode_name: selected_course_mode = course_mode break if not selected_course_mode: return self.get_enterprise_course_enrollment_page( request, enterprise_customer, course, course_modes) user_consent_needed = is_consent_required_for_user( enterprise_customer_user, course_id) if not selected_course_mode.get('premium') and not user_consent_needed: # For the audit course modes (audit, honor), where DSC is not # required, enroll the learner directly through enrollment API # client and redirect the learner to LMS courseware page. with transaction.atomic(): # Create the Enterprise backend database records for this course # enrollment. EnterpriseCourseEnrollment.objects.get_or_create( enterprise_customer_user=enterprise_customer_user, course_id=course_id, ) client = EnrollmentApiClient() client.enroll_user_in_course(request.user.username, course_id, selected_course_mode_name) return redirect(LMS_COURSEWARE_URL.format(course_id=course_id)) elif user_consent_needed: # For the audit course modes (audit, honor) or for the premium # course modes (Verified, Prof Ed) where DSC is required, redirect # the learner to course specific DSC with enterprise UUID from # there the learner will be directed to the ecommerce flow after # providing DSC. next_url = '{handle_consent_enrollment_url}?{query_string}'.format( handle_consent_enrollment_url=reverse( 'enterprise_handle_consent_enrollment', args=[enterprise_customer.uuid, course_id]), query_string=urlencode( {'course_mode': selected_course_mode_name})) return redirect('{grant_data_sharing_url}?{params}'.format( grant_data_sharing_url=reverse( 'grant_data_sharing_permissions'), params=urlencode({ 'next': next_url, 'enterprise_id': enterprise_customer.uuid, 'course_id': course_id, 'enrollment_deferred': True, }))) else: # For the premium course modes (Verified, Prof Ed) where DSC is # not required, redirect the enterprise learner to the ecommerce # flow in LMS. # Note: LMS start flow automatically detects the paid mode return redirect( LMS_START_PREMIUM_COURSE_FLOW_URL.format(course_id=course_id))
def get(self, request, enterprise_uuid, course_id): """ Handle the enrollment of enterprise learner in the provided course. Based on `enterprise_uuid` in URL, the view will decide which enterprise customer's course enrollment record should be created. Depending on the value of query parameter `course_mode` then learner will be either redirected to LMS dashboard for audit modes or redirected to ecommerce basket flow for payment of premium modes. """ # Verify that all necessary resources are present verify_edx_resources() enrollment_course_mode = request.GET.get('course_mode') # Redirect the learner to LMS dashboard in case no course mode is # provided as query parameter `course_mode` if not enrollment_course_mode: return redirect(LMS_DASHBOARD_URL) try: enrollment_client = EnrollmentApiClient() course_modes = enrollment_client.get_course_modes(course_id) except HttpClientError: logger.error( 'Failed to determine available course modes for course ID: %s', course_id) raise Http404 # Verify that the request user belongs to the enterprise against the # provided `enterprise_uuid`. enterprise_customer = get_enterprise_customer_or_404(enterprise_uuid) enterprise_customer_user = get_enterprise_customer_user( request.user.id, enterprise_customer.uuid) if not enterprise_customer_user: raise Http404 selected_course_mode = None for course_mode in course_modes: if course_mode['slug'] == enrollment_course_mode: selected_course_mode = course_mode break if not selected_course_mode: return redirect(LMS_DASHBOARD_URL) # Create the Enterprise backend database records for this course # enrollment EnterpriseCourseEnrollment.objects.update_or_create( enterprise_customer_user=enterprise_customer_user, course_id=course_id, defaults={ 'consent_granted': True, }) audit_modes = getattr(settings, 'ENTERPRISE_COURSE_ENROLLMENT_AUDIT_MODES', ['audit', 'honor']) if selected_course_mode['slug'] in audit_modes: # In case of Audit course modes enroll the learner directly through # enrollment API client and redirect the learner to dashboard. enrollment_api_client = EnrollmentApiClient() enrollment_api_client.enroll_user_in_course( request.user.username, course_id, selected_course_mode['slug']) return redirect(LMS_COURSEWARE_URL.format(course_id=course_id)) # redirect the enterprise learner to the ecommerce flow in LMS # Note: LMS start flow automatically detects the paid mode return redirect( LMS_START_PREMIUM_COURSE_FLOW_URL.format(course_id=course_id))