Ejemplo n.º 1
0
    def test_cert_generation_flag_on_pacing_toggle(self):
        """
        Verify that signal enables or disables self-generated certificates
        according to course-pacing.
        """
        course = CourseFactory.create(self_paced=False, emit_signals=True)
        assert not has_self_generated_certificates_enabled(course.id)

        course.self_paced = True
        self.store.update_item(course, self.user.id)
        assert has_self_generated_certificates_enabled(course.id)

        course.self_paced = False
        self.store.update_item(course, self.user.id)
        assert not has_self_generated_certificates_enabled(course.id)
Ejemplo n.º 2
0
def _cert_info(user, enrollment, cert_status):
    """
    Implements the logic for cert_info -- split out for testing.

    TODO: replace with a method that lives in the certificates app and combines this logic with
     lms.djangoapps.certificates.api.can_show_certificate_message and
     lms.djangoapps.courseware.views.get_cert_data

    Arguments:
        user (User): A user.
        enrollment (CourseEnrollment): A course enrollment.
        cert_status (dict): dictionary containing information about certificate status for the user

    Returns:
        dictionary containing:
            'status': one of 'generating', 'downloadable', 'notpassing', 'restricted', 'auditing',
                'processing', 'unverified', 'unavailable', or 'certificate_earned_but_not_available'
            'show_survey_button': bool
            'can_unenroll': if status allows for unenrollment

        The dictionary may also contain:
            'linked_in_url': url to add cert to LinkedIn profile
            'survey_url': url, only if course_overview.end_of_course_survey_url is not None
            'show_cert_web_view': bool if html web certs are enabled and there is an active web cert
            'cert_web_view_url': url if html web certs are enabled and there is an active web cert
            'download_url': url to download a cert
            'grade': if status is in 'generating', 'downloadable', 'notpassing', 'restricted',
                'auditing', or 'unverified'
    """
    # simplify the status for the template using this lookup table
    template_state = {
        CertificateStatuses.generating: 'generating',
        CertificateStatuses.downloadable: 'downloadable',
        CertificateStatuses.notpassing: 'notpassing',
        CertificateStatuses.restricted: 'restricted',
        CertificateStatuses.auditing: 'auditing',
        CertificateStatuses.audit_passing: 'auditing',
        CertificateStatuses.audit_notpassing: 'auditing',
        CertificateStatuses.unverified: 'unverified',
    }

    certificate_earned_but_not_available_status = 'certificate_earned_but_not_available'
    default_status = 'processing'

    default_info = {
        'status': default_status,
        'show_survey_button': False,
        'can_unenroll': True,
    }

    if cert_status is None or enrollment is None:
        return default_info

    course_overview = enrollment.course_overview if enrollment else None
    status = template_state.get(cert_status['status'], default_status)
    is_hidden_status = status in ('processing', 'generating', 'notpassing',
                                  'auditing')

    if _is_certificate_earned_but_not_available(course_overview, status):
        status = certificate_earned_but_not_available_status

    if (course_overview.certificates_display_behavior
            == CertificatesDisplayBehaviors.EARLY_NO_INFO
            and is_hidden_status):
        return default_info

    if not CourseMode.is_eligible_for_certificate(enrollment.mode,
                                                  status=status):
        return default_info

    if course_overview and access.is_beta_tester(user, course_overview.id):
        # Beta testers are not eligible for a course certificate
        return default_info

    status_dict = {
        'status': status,
        'mode': cert_status.get('mode', None),
        'linked_in_url': None,
        'can_unenroll': status not in DISABLE_UNENROLL_CERT_STATES,
    }

    if status != default_status and course_overview.end_of_course_survey_url is not None:
        status_dict.update({
            'show_survey_button':
            True,
            'survey_url':
            process_survey_link(course_overview.end_of_course_survey_url, user)
        })
    else:
        status_dict['show_survey_button'] = False

    if status == 'downloadable':
        # showing the certificate web view button if certificate is downloadable state and feature flags are enabled.
        if has_html_certificates_enabled(course_overview):
            if course_overview.has_any_active_web_certificate:
                status_dict.update({
                    'show_cert_web_view':
                    True,
                    'cert_web_view_url':
                    get_certificate_url(course_id=course_overview.id,
                                        uuid=cert_status['uuid'])
                })
            elif cert_status['download_url']:
                status_dict['download_url'] = cert_status['download_url']
            else:
                # don't show download certificate button if we don't have an active certificate for course
                status_dict['status'] = 'unavailable'
        elif 'download_url' not in cert_status:
            log.warning(
                "User %s has a downloadable cert for %s, but no download url",
                user.username, course_overview.id)
            return default_info
        else:
            status_dict['download_url'] = cert_status['download_url']

            # If enabled, show the LinkedIn "add to profile" button
            # Clicking this button sends the user to LinkedIn where they
            # can add the certificate information to their profile.
            linkedin_config = LinkedInAddToProfileConfiguration.current()
            if linkedin_config.is_enabled():
                status_dict[
                    'linked_in_url'] = linkedin_config.add_to_profile_url(
                        course_overview.display_name,
                        cert_status.get('mode'),
                        cert_status['download_url'],
                    )

    if status in {
            'generating', 'downloadable', 'notpassing', 'restricted',
            'auditing', 'unverified'
    }:
        cert_grade_percent = -1
        persisted_grade_percent = -1
        persisted_grade = CourseGradeFactory().read(user,
                                                    course=course_overview,
                                                    create_if_needed=False)
        if persisted_grade is not None:
            persisted_grade_percent = persisted_grade.percent

        if 'grade' in cert_status:
            cert_grade_percent = float(cert_status['grade'])

        if cert_grade_percent == -1 and persisted_grade_percent == -1:
            # Note: as of 11/20/2012, we know there are students in this state-- cs169.1x,
            # who need to be regraded (we weren't tracking 'notpassing' at first).
            # We can add a log.warning here once we think it shouldn't happen.
            return default_info
        grades_input = [cert_grade_percent, persisted_grade_percent]
        max_grade = (None if all(grade is None for grade in grades_input) else
                     max(filter(lambda x: x is not None, grades_input)))
        status_dict['grade'] = str(max_grade)

        # If the grade is passing, the status is one of these statuses, and request certificate
        # is enabled for a course then we need to provide the option to the learner
        cert_gen_enabled = (has_self_generated_certificates_enabled(
            course_overview.id) or auto_certificate_generation_enabled())
        passing_grade = persisted_grade and persisted_grade.passed
        if (status_dict['status'] != CertificateStatuses.downloadable
                and cert_gen_enabled and passing_grade
                and course_overview.has_any_active_web_certificate):
            status_dict['status'] = CertificateStatuses.requesting

    return status_dict
def _section_certificates(course):
    """Section information for the certificates panel.

    The certificates panel allows global staff to generate
    example certificates and enable self-generated certificates
    for a course.

    Arguments:
        course (Course)

    Returns:
        dict

    """
    example_cert_status = None
    html_cert_enabled = certs_api.has_html_certificates_enabled(course)
    if html_cert_enabled:
        can_enable_for_course = True
    else:
        example_cert_status = certs_api.example_certificates_status(course.id)

        # Allow the user to enable self-generated certificates for students
        # *only* once a set of example certificates has been successfully generated.
        # If certificates have been misconfigured for the course (for example, if
        # the PDF template hasn't been uploaded yet), then we don't want
        # to turn on self-generated certificates for students!
        can_enable_for_course = (
            example_cert_status is not None and
            all(
                cert_status['status'] == 'success'
                for cert_status in example_cert_status
            )
        )
    instructor_generation_enabled = settings.FEATURES.get('CERTIFICATES_INSTRUCTOR_GENERATION', False)
    certificate_statuses_with_count = {
        certificate['status']: certificate['count']
        for certificate in GeneratedCertificate.get_unique_statuses(course_key=course.id)
    }

    return {
        'section_key': 'certificates',
        'section_display_name': _('Certificates'),
        'example_certificate_status': example_cert_status,
        'can_enable_for_course': can_enable_for_course,
        'enabled_for_course': certs_api.has_self_generated_certificates_enabled(course.id),
        'is_self_paced': course.self_paced,
        'instructor_generation_enabled': instructor_generation_enabled,
        'html_cert_enabled': html_cert_enabled,
        'active_certificate': certs_api.get_active_web_certificate(course),
        'certificate_statuses_with_count': certificate_statuses_with_count,
        'status': CertificateStatuses,
        'certificate_generation_history':
            CertificateGenerationHistory.objects.filter(course_id=course.id).order_by("-created"),
        'urls': {
            'enable_certificate_generation': reverse(
                'enable_certificate_generation',
                kwargs={'course_id': course.id}
            ),
            'start_certificate_generation': reverse(
                'start_certificate_generation',
                kwargs={'course_id': course.id}
            ),
            'start_certificate_regeneration': reverse(
                'start_certificate_regeneration',
                kwargs={'course_id': course.id}
            ),
            'list_instructor_tasks_url': reverse(
                'list_instructor_tasks',
                kwargs={'course_id': course.id}
            ),
        }
    }
Ejemplo n.º 4
0
 def _assert_enabled_for_course(self, course_key, expect_enabled):
     """Check that self-generated certificates are enabled or disabled for the course. """
     actual_enabled = has_self_generated_certificates_enabled(course_key)
     assert expect_enabled == actual_enabled