def _enroll_entitlement(self, entitlement, course_run_key, user): """ Internal method to handle the details of enrolling a User in a Course Run. Returns a response object is there is an error or exception, None otherwise """ try: unexpired_paid_modes = [ mode.slug for mode in CourseMode.paid_modes_for_course(course_run_key) ] can_upgrade = unexpired_paid_modes and entitlement.mode in unexpired_paid_modes enrollment = CourseEnrollment.enroll(user=user, course_key=course_run_key, mode=entitlement.mode, check_access=True, can_upgrade=can_upgrade) except AlreadyEnrolledError: enrollment = CourseEnrollment.get_enrollment(user, course_run_key) if enrollment.mode == entitlement.mode: entitlement.set_enrollment(enrollment) elif enrollment.mode not in unexpired_paid_modes: enrollment.update_enrollment(mode=entitlement.mode) entitlement.set_enrollment(enrollment) # Else the User is already enrolled in another paid Mode and we should # not do anything else related to Entitlements. except CourseEnrollmentException: message = ( 'Course Entitlement Enroll for {username} failed for course: {course_id}, ' 'mode: {mode}, and entitlement: {entitlement}').format( username=user.username, course_id=course_run_key, mode=entitlement.mode, entitlement=entitlement.uuid) return Response(status=status.HTTP_400_BAD_REQUEST, data={'message': message}) entitlement.set_enrollment(enrollment) return None
def instructor_dashboard_2(request, course_id): # lint-amnesty, pylint: disable=too-many-statements """ Display the instructor dashboard for a course. """ try: course_key = CourseKey.from_string(course_id) except InvalidKeyError: log.error( "Unable to find course with course key %s while loading the Instructor Dashboard.", course_id) return HttpResponseServerError() course = get_course_by_id(course_key, depth=0) access = { 'admin': request.user.is_staff, 'instructor': bool(has_access(request.user, 'instructor', course)), 'finance_admin': CourseFinanceAdminRole(course_key).has_user(request.user), 'sales_admin': CourseSalesAdminRole(course_key).has_user(request.user), 'staff': bool(has_access(request.user, 'staff', course)), 'forum_admin': has_forum_access(request.user, course_key, FORUM_ROLE_ADMINISTRATOR), 'data_researcher': request.user.has_perm(permissions.CAN_RESEARCH, course_key), } if not request.user.has_perm(permissions.VIEW_DASHBOARD, course_key): raise Http404() is_white_label = CourseMode.is_white_label(course_key) # lint-amnesty, pylint: disable=unused-variable reports_enabled = configuration_helpers.get_value('SHOW_ECOMMERCE_REPORTS', False) # lint-amnesty, pylint: disable=unused-variable sections = [] if access['staff']: sections.extend([ _section_course_info(course, access), _section_membership(course, access), _section_cohort_management(course, access), _section_discussions_management(course, access), _section_student_admin(course, access), ]) if access['data_researcher']: sections.append(_section_data_download(course, access)) analytics_dashboard_message = None if show_analytics_dashboard_message(course_key) and (access['staff'] or access['instructor']): # Construct a URL to the external analytics dashboard analytics_dashboard_url = '{}/courses/{}'.format( settings.ANALYTICS_DASHBOARD_URL, str(course_key)) link_start = HTML("<a href=\"{}\" rel=\"noopener\" target=\"_blank\">" ).format(analytics_dashboard_url) analytics_dashboard_message = _( "To gain insights into student enrollment and participation {link_start}" "visit {analytics_dashboard_name}, our new course analytics product{link_end}." ) analytics_dashboard_message = Text(analytics_dashboard_message).format( link_start=link_start, link_end=HTML("</a>"), analytics_dashboard_name=settings.ANALYTICS_DASHBOARD_NAME) # Temporarily show the "Analytics" section until we have a better way of linking to Insights sections.append(_section_analytics(course, access)) # Check if there is corresponding entry in the CourseMode Table related to the Instructor Dashboard course course_mode_has_price = False # lint-amnesty, pylint: disable=unused-variable paid_modes = CourseMode.paid_modes_for_course(course_key) if len(paid_modes) == 1: course_mode_has_price = True elif len(paid_modes) > 1: log.error( "Course %s has %s course modes with payment options. Course must only have " "one paid course mode to enable eCommerce options.", str(course_key), len(paid_modes)) if access['instructor'] and is_enabled_for_course(course_key): sections.insert(3, _section_extensions(course)) # Gate access to course email by feature flag & by course-specific authorization if is_bulk_email_feature_enabled(course_key) and (access['staff'] or access['instructor']): sections.append(_section_send_email(course, access)) # Gate access to Special Exam tab depending if either timed exams or proctored exams # are enabled in the course user_has_access = any([ request.user.is_staff, CourseStaffRole(course_key).has_user(request.user), CourseInstructorRole(course_key).has_user(request.user) ]) course_has_special_exams = course.enable_proctored_exams or course.enable_timed_exams can_see_special_exams = course_has_special_exams and user_has_access and settings.FEATURES.get( 'ENABLE_SPECIAL_EXAMS', False) if can_see_special_exams: sections.append(_section_special_exams(course, access)) # Certificates panel # This is used to generate example certificates # and enable self-generated certificates for a course. # Note: This is hidden for all CCXs certs_enabled = CertificateGenerationConfiguration.current( ).enabled and not hasattr(course_key, 'ccx') if certs_enabled and access['admin']: sections.append(_section_certificates(course)) openassessment_blocks = modulestore().get_items( course_key, qualifiers={'category': 'openassessment'}) # filter out orphaned openassessment blocks openassessment_blocks = [ block for block in openassessment_blocks if block.parent is not None ] if len(openassessment_blocks) > 0 and access['staff']: sections.append( _section_open_response_assessment(request, course, openassessment_blocks, access)) disable_buttons = not CourseEnrollment.objects.is_small_course(course_key) certificate_white_list = CertificateWhitelist.get_certificate_white_list( course_key) generate_certificate_exceptions_url = reverse( 'generate_certificate_exceptions', kwargs={ 'course_id': str(course_key), 'generate_for': '' }) generate_bulk_certificate_exceptions_url = reverse( 'generate_bulk_certificate_exceptions', kwargs={'course_id': str(course_key)}) certificate_exception_view_url = reverse( 'certificate_exception_view', kwargs={'course_id': str(course_key)}) certificate_invalidation_view_url = reverse( 'certificate_invalidation_view', kwargs={'course_id': str(course_key)}) certificate_invalidations = CertificateInvalidation.get_certificate_invalidations( course_key) context = { 'course': course, 'studio_url': get_studio_url(course, 'course'), 'sections': sections, 'disable_buttons': disable_buttons, 'analytics_dashboard_message': analytics_dashboard_message, 'certificate_white_list': certificate_white_list, 'certificate_invalidations': certificate_invalidations, 'generate_certificate_exceptions_url': generate_certificate_exceptions_url, 'generate_bulk_certificate_exceptions_url': generate_bulk_certificate_exceptions_url, 'certificate_exception_view_url': certificate_exception_view_url, 'certificate_invalidation_view_url': certificate_invalidation_view_url, 'xqa_server': settings.FEATURES.get('XQA_SERVER', "http://your_xqa_server.com"), } return render_to_response( 'instructor/instructor_dashboard_2/instructor_dashboard_2.html', context)
def create_order(request): """ This endpoint is named 'create_order' for backward compatibility, but its actual use is to add a single product to the user's cart and request immediate checkout. """ course_id = request.POST['course_id'] course_id = CourseKey.from_string(course_id) donation_for_course = request.session.get('donation_for_course', {}) contribution = request.POST.get( "contribution", donation_for_course.get(six.text_type(course_id), 0)) try: amount = decimal.Decimal(contribution).quantize( decimal.Decimal('.01'), rounding=decimal.ROUND_DOWN) except decimal.InvalidOperation: return HttpResponseBadRequest(_("Selected price is not valid number.")) current_mode = None sku = request.POST.get('sku', None) if sku: try: current_mode = CourseMode.objects.get(sku=sku) except CourseMode.DoesNotExist: log.exception(u'Failed to find CourseMode with SKU [%s].', sku) if not current_mode: # Check if there are more than 1 paid(mode with min_price>0 e.g verified/professional/no-id-professional) modes # for course exist then choose the first one paid_modes = CourseMode.paid_modes_for_course(course_id) if paid_modes: if len(paid_modes) > 1: log.warning( u"Multiple paid course modes found for course '%s' for create order request", course_id) current_mode = paid_modes[0] # Make sure this course has a paid mode if not current_mode: log.warning( u"Create order requested for course '%s' without a paid mode.", course_id) return HttpResponseBadRequest( _("This course doesn't support paid certificates")) if CourseMode.is_professional_mode(current_mode): amount = current_mode.min_price if amount < current_mode.min_price: return HttpResponseBadRequest( _("No selected price or selected price is below minimum.")) # if request.POST doesn't contain 'processor' then the service's default payment processor will be used. payment_data = checkout_with_ecommerce_service( request.user, course_id, current_mode, request.POST.get('processor')) if 'processor' not in request.POST: # (XCOM-214) To be removed after release. # the absence of this key in the POST payload indicates that the request was initiated from # a stale js client, which expects a response containing only the 'payment_form_data' part of # the payment data result. payment_data = payment_data['payment_form_data'] return JsonResponse(payment_data)