Ejemplo n.º 1
0
    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)
Ejemplo n.º 3
0
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)