Exemple #1
0
def get_course_date_blocks(course, user):
    """
    Return the list of blocks to display on the course info page,
    sorted by date.
    """
    block_classes = [
        CourseEndDate,
        CourseStartDate,
        TodaysDate,
        VerificationDeadlineDate,
        VerifiedUpgradeDeadlineDate,
    ]
    if certs_api.get_active_web_certificate(course):
        block_classes.insert(0, CertificateAvailableDate)

    blocks = (cls(course, user) for cls in block_classes)

    def block_key_fn(block):
        """
        If the block's date is None, return the maximum datetime in order
        to force it to the end of the list of displayed blocks.
        """
        if block.date is None:
            return datetime.max.replace(tzinfo=pytz.UTC)
        return block.date

    return sorted((b for b in blocks if b.is_enabled), key=block_key_fn)
def get_course_date_blocks(course, user, request=None, include_access=False,
                           include_past_dates=False, num_assignments=None):
    """
    Return the list of blocks to display on the course info page,
    sorted by date.
    """
    block_classes = [
        CourseEndDate,
        CourseStartDate,
        TodaysDate,
        VerificationDeadlineDate,
        VerifiedUpgradeDeadlineDate,
    ]
    if not course.self_paced and certs_api.get_active_web_certificate(course):
        block_classes.insert(0, CertificateAvailableDate)

    blocks = [cls(course, user) for cls in block_classes]
    if RELATIVE_DATES_FLAG.is_enabled(course.id):
        blocks.append(CourseExpiredDate(course, user))
        blocks.extend(get_course_assignment_date_blocks(
            course, user, request, num_return=num_assignments,
            include_access=include_access, include_past_dates=include_past_dates,
        ))

    return sorted((b for b in blocks if b.date and (b.is_enabled or include_past_dates)), key=date_block_key_fn)
Exemple #3
0
def get_course_date_blocks(course, user):
    """
    Return the list of blocks to display on the course info page,
    sorted by date.
    """
    block_classes = [
        CourseEndDate,
        CourseStartDate,
        TodaysDate,
        VerificationDeadlineDate,
        VerifiedUpgradeDeadlineDate,
    ]
    if certs_api.get_active_web_certificate(course):
        block_classes.insert(0, CertificateAvailableDate)

    blocks = (cls(course, user) for cls in block_classes)

    def block_key_fn(block):
        """
        If the block's date is None, return the maximum datetime in order
        to force it to the end of the list of displayed blocks.
        """
        if block.date is None:
            return datetime.max.replace(tzinfo=pytz.UTC)
        return block.date
    return sorted((b for b in blocks if b.is_enabled), key=block_key_fn)
    def _create_from_course(course):
        """
        Creates a CourseOverview object from a CourseDescriptor.

        Does not touch the database, simply constructs and returns an overview
        from the given course.

        Arguments:
            course (CourseDescriptor): any course descriptor object

        Returns:
            CourseOverview: overview extracted from the given course
        """
        from lms.djangoapps.certificates.api import get_active_web_certificate
        from lms.djangoapps.courseware.courses import course_image_url

        # Workaround for a problem discovered in https://openedx.atlassian.net/browse/TNL-2806.
        # If the course has a malformed grading policy such that
        # course._grading_policy['GRADE_CUTOFFS'] = {}, then
        # course.lowest_passing_grade will raise a ValueError.
        # Work around this for now by defaulting to None.
        try:
            lowest_passing_grade = course.lowest_passing_grade
        except ValueError:
            lowest_passing_grade = None

        return CourseOverview(
            id=course.id,
            _location=course.location,
            display_name=course.display_name,
            display_number_with_default=course.display_number_with_default,
            display_org_with_default=course.display_org_with_default,

            start=course.start,
            end=course.end,
            advertised_start=course.advertised_start,

            course_image_url=course_image_url(course),
            facebook_url=course.facebook_url,
            social_sharing_url=course.social_sharing_url,

            certificates_display_behavior=course.certificates_display_behavior,
            certificates_show_before_end=course.certificates_show_before_end,
            cert_html_view_enabled=course.cert_html_view_enabled,
            has_any_active_web_certificate=(get_active_web_certificate(course) is not None),
            cert_name_short=course.cert_name_short,
            cert_name_long=course.cert_name_long,
            lowest_passing_grade=lowest_passing_grade,
            end_of_course_survey_url=course.end_of_course_survey_url,

            days_early_for_beta=course.days_early_for_beta,
            mobile_available=course.mobile_available,
            visible_to_staff_only=course.visible_to_staff_only,
            _pre_requisite_courses_json=json.dumps(course.pre_requisite_courses)
        )
Exemple #5
0
    def _create_from_course(course):
        """
        Creates a CourseOverview object from a CourseDescriptor.

        Does not touch the database, simply constructs and returns an overview
        from the given course.

        Arguments:
            course (CourseDescriptor): any course descriptor object

        Returns:
            CourseOverview: overview extracted from the given course
        """
        from lms.djangoapps.certificates.api import get_active_web_certificate
        from lms.djangoapps.courseware.courses import course_image_url

        # Workaround for a problem discovered in https://openedx.atlassian.net/browse/TNL-2806.
        # If the course has a malformed grading policy such that
        # course._grading_policy['GRADE_CUTOFFS'] = {}, then
        # course.lowest_passing_grade will raise a ValueError.
        # Work around this for now by defaulting to None.
        try:
            lowest_passing_grade = course.lowest_passing_grade
        except ValueError:
            lowest_passing_grade = None

        return CourseOverview(
            id=course.id,
            _location=course.location,
            display_name=course.display_name,
            display_number_with_default=course.display_number_with_default,
            display_org_with_default=course.display_org_with_default,
            start=course.start,
            end=course.end,
            advertised_start=course.advertised_start,
            course_image_url=course_image_url(course),
            facebook_url=course.facebook_url,
            social_sharing_url=course.social_sharing_url,
            certificates_display_behavior=course.certificates_display_behavior,
            certificates_show_before_end=course.certificates_show_before_end,
            cert_html_view_enabled=course.cert_html_view_enabled,
            has_any_active_web_certificate=(get_active_web_certificate(course)
                                            is not None),
            cert_name_short=course.cert_name_short,
            cert_name_long=course.cert_name_long,
            lowest_passing_grade=lowest_passing_grade,
            end_of_course_survey_url=course.end_of_course_survey_url,
            days_early_for_beta=course.days_early_for_beta,
            mobile_available=course.mobile_available,
            visible_to_staff_only=course.visible_to_staff_only,
            _pre_requisite_courses_json=json.dumps(
                course.pre_requisite_courses))
Exemple #6
0
    def _create_from_course(course):
        """
        Creates a CourseOverview object from a CourseDescriptor.

        Does not touch the database, simply constructs and returns an overview
        from the given course.

        Arguments:
            course (CourseDescriptor): any course descriptor object

        Returns:
            CourseOverview: overview extracted from the given course
        """
        from lms.djangoapps.certificates.api import get_active_web_certificate
        from lms.djangoapps.courseware.courses import course_image_url

        return CourseOverview(
            id=course.id,
            _location=course.location,
            display_name=course.display_name,
            display_number_with_default=course.display_number_with_default,
            display_org_with_default=course.display_org_with_default,

            start=course.start,
            end=course.end,
            advertised_start=course.advertised_start,

            course_image_url=course_image_url(course),
            facebook_url=course.facebook_url,
            social_sharing_url=course.social_sharing_url,

            certificates_display_behavior=course.certificates_display_behavior,
            certificates_show_before_end=course.certificates_show_before_end,
            cert_html_view_enabled=course.cert_html_view_enabled,
            has_any_active_web_certificate=(get_active_web_certificate(course) is not None),
            cert_name_short=course.cert_name_short,
            cert_name_long=course.cert_name_long,
            lowest_passing_grade=course.lowest_passing_grade,
            end_of_course_survey_url=course.end_of_course_survey_url,

            days_early_for_beta=course.days_early_for_beta,
            mobile_available=course.mobile_available,
            visible_to_staff_only=course.visible_to_staff_only,
            _pre_requisite_courses_json=json.dumps(course.pre_requisite_courses)
        )
Exemple #7
0
def get_course_date_blocks(course,
                           user,
                           request=None,
                           include_access=False,
                           include_past_dates=False,
                           num_assignments=None):
    """
    Return the list of blocks to display on the course info page,
    sorted by date.
    """
    blocks = []
    if RELATIVE_DATES_FLAG.is_enabled(course.id):
        blocks.append(CourseExpiredDate(course, user))
        blocks.extend(
            get_course_assignment_date_blocks(
                course,
                user,
                request,
                num_return=num_assignments,
                include_access=include_access,
                include_past_dates=include_past_dates,
            ))

    # Adding these in after the assignment blocks so in the case multiple blocks have the same date,
    # these blocks will be sorted to come after the assignments. See https://openedx.atlassian.net/browse/AA-158
    default_block_classes = [
        CourseEndDate,
        CourseStartDate,
        TodaysDate,
        VerificationDeadlineDate,
        VerifiedUpgradeDeadlineDate,
    ]
    if not course.self_paced and certs_api.get_active_web_certificate(course):
        block_classes.insert(0, CertificateAvailableDate)

    blocks.extend([cls(course, user) for cls in default_block_classes])

    return sorted(
        (b for b in blocks if b.date and (b.is_enabled or include_past_dates)),
        key=date_block_key_fn)
    def __init__(self,
                 course,
                 user,
                 course_desc,
                 path_builder=None,
                 preview_mode=None):
        self.course_id = course.id

        self.path_builder = path_builder
        self.certificate_data = get_active_web_certificate(
            course, preview_mode)
        self.cert = self._get_user_certificate(user,
                                               course,
                                               preview_mode=preview_mode)
        course_title_from_cert = self.certificate_data.get('course_title')
        self.course_name = course_title_from_cert if course_title_from_cert else course.display_name
        self.course_desc = course_desc
        self.organizations = organization_api.get_course_organizations(
            course_id=self.course_id)
        self.sponsors = None

        self.colors = {
            'base': (225 / 255.0, 0 / 255.0, 67 / 255.0),
            'grey-dark': (66 / 255.0, 74 / 255.0, 82 / 255.0),
            'grey-light': (113 / 255.0, 113 / 255.0, 113 / 255.0),
        }

        self.temp_file = NamedTemporaryFile(suffix='-cert.pdf')
        self.is_english = not contains_rtl_text(self.course_name)
        self.user_profile_name = self._get_user_name(user)

        self.left_panel_center = 1.6
        self.ctx = None
        self.size = None
        self.font = None
        self.font_size = None
    def check_course_overview_against_course(self, course):
        """
        Compares a CourseOverview object against its corresponding
        CourseDescriptor object.

        Specifically, given a course, test that data within the following three
        objects match each other:
         - the CourseDescriptor itself
         - a CourseOverview that was newly constructed from _create_from_course
         - a CourseOverview that was loaded from the MySQL database

        Arguments:
            course (CourseDescriptor): the course to be checked.
        """

        def get_seconds_since_epoch(date_time):
            """
            Returns the number of seconds between the Unix Epoch and the given
                datetime. If the given datetime is None, return None.

            Arguments:
                date_time (datetime): the datetime in question.
            """
            if date_time is None:
                return None
            epoch = datetime.datetime.utcfromtimestamp(0).replace(tzinfo=pytz.utc)
            return math.floor((date_time - epoch).total_seconds())

        # Load the CourseOverview from the cache twice. The first load will be a cache miss (because the cache
        # is empty) so the course will be newly created with CourseOverviewDescriptor.create_from_course. The second
        # load will be a cache hit, so the course will be loaded from the cache.
        course_overview_cache_miss = CourseOverview.get_from_id(course.id)
        course_overview_cache_hit = CourseOverview.get_from_id(course.id)

        # Test if value of these attributes match between the three objects
        fields_to_test = [
            'id',
            'display_name',
            'display_number_with_default',
            'display_org_with_default',
            'advertised_start',
            'facebook_url',
            'social_sharing_url',
            'certificates_display_behavior',
            'certificates_show_before_end',
            'cert_name_short',
            'cert_name_long',
            'lowest_passing_grade',
            'end_of_course_survey_url',
            'mobile_available',
            'visible_to_staff_only',
            'location',
            'number',
            'url_name',
            'display_name_with_default',
            'start_date_is_still_default',
            'pre_requisite_courses',
        ]
        for attribute_name in fields_to_test:
            course_value = getattr(course, attribute_name)
            cache_miss_value = getattr(course_overview_cache_miss, attribute_name)
            cache_hit_value = getattr(course_overview_cache_hit, attribute_name)
            self.assertEqual(course_value, cache_miss_value)
            self.assertEqual(cache_miss_value, cache_hit_value)

        # Test if return values for all methods are equal between the three objects
        methods_to_test = [
            ('clean_id', ()),
            ('clean_id', ('#',)),
            ('has_ended', ()),
            ('has_started', ()),
            ('start_datetime_text', ('SHORT_DATE',)),
            ('start_datetime_text', ('DATE_TIME',)),
            ('end_datetime_text', ('SHORT_DATE',)),
            ('end_datetime_text', ('DATE_TIME',)),
            ('may_certify', ()),
        ]
        for method_name, method_args in methods_to_test:
            course_value = getattr(course, method_name)(*method_args)
            cache_miss_value = getattr(course_overview_cache_miss, method_name)(*method_args)
            cache_hit_value = getattr(course_overview_cache_hit, method_name)(*method_args)
            self.assertEqual(course_value, cache_miss_value)
            self.assertEqual(cache_miss_value, cache_hit_value)

        # Other values to test
        # Note: we test the start and end attributes here instead of in
        # fields_to_test, because I ran into trouble while testing datetimes
        # for equality. When writing and reading dates from databases, the
        # resulting values are often off by fractions of a second. So, as a
        # workaround, we simply test if the start and end times are the same
        # number of seconds from the Unix epoch.
        others_to_test = [(
            course_image_url(course),
            course_overview_cache_miss.course_image_url,
            course_overview_cache_hit.course_image_url
        ), (
            get_active_web_certificate(course) is not None,
            course_overview_cache_miss.has_any_active_web_certificate,
            course_overview_cache_hit.has_any_active_web_certificate

        ), (
            get_seconds_since_epoch(course.start),
            get_seconds_since_epoch(course_overview_cache_miss.start),
            get_seconds_since_epoch(course_overview_cache_hit.start),
        ), (
            get_seconds_since_epoch(course.end),
            get_seconds_since_epoch(course_overview_cache_miss.end),
            get_seconds_since_epoch(course_overview_cache_hit.end),
        )]
        for (course_value, cache_miss_value, cache_hit_value) in others_to_test:
            self.assertEqual(course_value, cache_miss_value)
            self.assertEqual(cache_miss_value, cache_hit_value)
 def is_allowed(self):
     return (can_show_certificate_available_date_field(self.course)
             and self.has_certificate_modes
             and get_active_web_certificate(self.course))
Exemple #11
0
    def check_course_overview_against_course(self, course):
        """
        Compares a CourseOverview object against its corresponding
        CourseDescriptor object.

        Specifically, given a course, test that data within the following three
        objects match each other:
         - the CourseDescriptor itself
         - a CourseOverview that was newly constructed from _create_or_update
         - a CourseOverview that was loaded from the MySQL database

        Arguments:
            course (CourseDescriptor): the course to be checked.
        """

        def get_seconds_since_epoch(date_time):
            """
            Returns the number of seconds between the Unix Epoch and the given
                datetime. If the given datetime is None, return None.

            Arguments:
                date_time (datetime): the datetime in question.
            """
            if date_time is None:
                return None
            epoch = datetime.datetime.utcfromtimestamp(0).replace(tzinfo=pytz.utc)
            return math.floor((date_time - epoch).total_seconds())

        # Load the CourseOverview from the cache twice. The first load will be a cache miss (because the cache
        # is empty) so the course will be newly created with CourseOverview._create_or_update. The second
        # load will be a cache hit, so the course will be loaded from the cache.
        course_overview_cache_miss = CourseOverview.get_from_id(course.id)
        course_overview_cache_hit = CourseOverview.get_from_id(course.id)

        # Test if value of these attributes match between the three objects
        fields_to_test = [
            'id',
            'display_name',
            'display_number_with_default',
            'display_org_with_default',
            'advertised_start',
            'social_sharing_url',
            'certificates_display_behavior',
            'certificates_show_before_end',
            'cert_name_short',
            'cert_name_long',
            'lowest_passing_grade',
            'end_of_course_survey_url',
            'mobile_available',
            'visible_to_staff_only',
            'location',
            'number',
            'url_name',
            'display_name_with_default',
            'display_name_with_default_escaped',
            'start_date_is_still_default',
            'pre_requisite_courses',
            'enrollment_domain',
            'invitation_only',
            'max_student_enrollments_allowed',
            'catalog_visibility',
        ]
        for attribute_name in fields_to_test:
            course_value = getattr(course, attribute_name)
            cache_miss_value = getattr(course_overview_cache_miss, attribute_name)
            cache_hit_value = getattr(course_overview_cache_hit, attribute_name)
            self.assertEqual(course_value, cache_miss_value)
            self.assertEqual(cache_miss_value, cache_hit_value)

        # Test if return values for all methods are equal between the three objects
        methods_to_test = [
            ('clean_id', ()),
            ('clean_id', ('#',)),
            ('has_ended', ()),
            ('has_started', ()),
            ('may_certify', ()),
        ]
        for method_name, method_args in methods_to_test:
            course_value = getattr(course, method_name)(*method_args)
            cache_miss_value = getattr(course_overview_cache_miss, method_name)(*method_args)
            cache_hit_value = getattr(course_overview_cache_hit, method_name)(*method_args)
            self.assertEqual(course_value, cache_miss_value)
            self.assertEqual(cache_miss_value, cache_hit_value)

        # Other values to test

        # Note: we test the time-related attributes here instead of in
        # fields_to_test, because we run into trouble while testing datetimes
        # for equality. When writing and reading dates from databases, the
        # resulting values are often off by fractions of a second. So, as a
        # workaround, we simply test if the start and end times are the same
        # number of seconds from the Unix epoch.
        time_field_accessor = lambda object, field_name: get_seconds_since_epoch(getattr(object, field_name))

        # The course about fields are accessed through the CourseDetail
        # class for the course module, and stored as attributes on the
        # CourseOverview objects.
        course_about_accessor = lambda object, field_name: CourseDetails.fetch_about_attribute(object.id, field_name)

        others_to_test = [
            ('start', time_field_accessor, time_field_accessor),
            ('end', time_field_accessor, time_field_accessor),
            ('enrollment_start', time_field_accessor, time_field_accessor),
            ('enrollment_end', time_field_accessor, time_field_accessor),
            ('announcement', time_field_accessor, time_field_accessor),

            ('short_description', course_about_accessor, getattr),
            ('effort', course_about_accessor, getattr),
            (
                'video',
                lambda c, __: CourseDetails.fetch_video_url(c.id),
                lambda c, __: c.course_video_url,
            ),
            (
                'course_image_url',
                lambda c, __: course_image_url(c),
                getattr,
            ),
            (
                'has_any_active_web_certificate',
                lambda c, field_name: get_active_web_certificate(c) is not None,
                getattr,
            ),
        ]
        for attribute_name, course_accessor, course_overview_accessor in others_to_test:
            course_value = course_accessor(course, attribute_name)
            cache_miss_value = course_overview_accessor(course_overview_cache_miss, attribute_name)
            cache_hit_value = course_overview_accessor(course_overview_cache_hit, attribute_name)
            self.assertEqual(course_value, cache_miss_value)
            self.assertEqual(cache_miss_value, cache_hit_value)

        # test tabs for both cached miss and cached hit courses
        for course_overview in [course_overview_cache_miss, course_overview_cache_hit]:
            course_overview_tabs = course_overview.tabs.all()
            course_resp_tabs = {tab.tab_id for tab in course_overview_tabs}
            self.assertEqual(self.COURSE_OVERVIEW_TABS, course_resp_tabs)
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.cert_generation_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': {
            'generate_example_certificates': reverse(
                'generate_example_certificates',
                kwargs={'course_id': course.id}
            ),
            '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}
            ),
        }
    }
Exemple #13
0
def render_html_view(request, course_id, certificate=None):  # pylint: disable=too-many-statements
    """
    This public view generates an HTML representation of the specified user and course
    If a certificate is not available, we display a "Sorry!" screen instead
    It can be overridden by setting `OVERRIDE_RENDER_CERTIFICATE_VIEW` to an alternative implementation.
    """
    user = certificate.user if certificate else request.user
    user_id = user.id
    preview_mode = request.GET.get('preview', None)
    platform_name = configuration_helpers.get_value("platform_name", settings.PLATFORM_NAME)
    configuration = CertificateHtmlViewConfiguration.get_config()

    # Kick the user back to the "Invalid" screen if the feature is disabled globally
    if not settings.FEATURES.get('CERTIFICATES_HTML_VIEW', False):
        return _render_invalid_certificate(request, course_id, platform_name, configuration)

    # Load the course and user objects
    try:
        course_key = CourseKey.from_string(course_id)
        course = get_course_by_id(course_key)

    # For any course or user exceptions, kick the user back to the "Invalid" screen
    except (InvalidKeyError, Http404) as exception:
        error_str = (
            "Invalid cert: error finding course %s "
            "Specific error: %s"
        )
        log.info(error_str, course_id, str(exception))
        return _render_invalid_certificate(request, course_id, platform_name, configuration)

    course_overview = get_course_overview_or_none(course_key)

    # Kick the user back to the "Invalid" screen if the feature is disabled for the course
    if not course.cert_html_view_enabled:
        log.info(
            "Invalid cert: HTML certificates disabled for %s. User id: %d",
            course_id,
            user_id,
        )
        return _render_invalid_certificate(request, course_id, platform_name, configuration)

    # Load user's certificate
    user_certificate = _get_user_certificate(request, user, course_key, course_overview, preview_mode)
    if not user_certificate:
        log.info(
            "Invalid cert: User %d does not have eligible cert for %s.",
            user_id,
            course_id,
        )
        return _render_invalid_certificate(request, course_id, platform_name, configuration)

    # Get the active certificate configuration for this course
    # If we do not have an active certificate, we'll need to send the user to the "Invalid" screen
    # Passing in the 'preview' parameter, if specified, will return a configuration, if defined
    active_configuration = get_active_web_certificate(course, preview_mode)
    if active_configuration is None:
        log.info(
            "Invalid cert: course %s does not have an active configuration. User id: %d",
            course_id,
            user_id,
        )
        return _render_invalid_certificate(request, course_id, platform_name, configuration)

    # Get data from Discovery service that will be necessary for rendering this Certificate.
    catalog_data = _get_catalog_data_for_course(course_key)

    # Determine whether to use the standard or custom template to render the certificate.
    custom_template = None
    custom_template_language = None
    if settings.FEATURES.get('CUSTOM_CERTIFICATE_TEMPLATES_ENABLED', False):
        log.info("Custom certificate for course %s", course_id)
        custom_template, custom_template_language = _get_custom_template_and_language(
            course.id,
            user_certificate.mode,
            catalog_data.pop('content_language', None)
        )

    # Determine the language that should be used to render the certificate.
    # For the standard certificate template, use the user language. For custom templates, use
    # the language associated with the template.
    user_language = translation.get_language()
    certificate_language = custom_template_language if custom_template else user_language

    log.info(
        "certificate language is: %s for the course: %s",
        certificate_language,
        course_key
    )

    # Generate the certificate context in the correct language, then render the template.
    with translation.override(certificate_language):
        context = {'user_language': user_language}

        _update_context_with_basic_info(context, course_id, platform_name, configuration)

        context['certificate_data'] = active_configuration

        # Append/Override the existing view context values with any mode-specific ConfigurationModel values
        context.update(configuration.get(user_certificate.mode, {}))

        # Append organization info
        _update_organization_context(context, course)

        # Append course info
        _update_course_context(request, context, course, platform_name)

        # Append course run info from discovery
        context.update(catalog_data)

        # Append user info
        _update_context_with_user_info(context, user, user_certificate)

        # Append social sharing info
        _update_social_context(request, context, course, user_certificate, platform_name)

        # Append/Override the existing view context values with certificate specific values
        _update_certificate_context(context, course, course_overview, user_certificate, platform_name)

        # Append badge info
        _update_badge_context(context, course, user)

        # Add certificate header/footer data to current context
        context.update(get_certificate_header_context(is_secure=request.is_secure()))
        context.update(get_certificate_footer_context())

        # Append/Override the existing view context values with any course-specific static values from Advanced Settings
        context.update(course.cert_html_view_overrides)

        # Track certificate view events
        _track_certificate_events(request, course, user, user_certificate)

        try:
            # .. filter_implemented_name: CertificateRenderStarted
            # .. filter_type: org.openedx.learning.certificate.render.started.v1
            context, custom_template = CertificateRenderStarted.run_filter(
                context=context,
                custom_template=custom_template,
            )
        except CertificateRenderStarted.RenderAlternativeInvalidCertificate as exc:
            response = _render_invalid_certificate(
                request,
                course_id,
                platform_name,
                configuration,
                cert_path=exc.template_name or INVALID_CERTIFICATE_TEMPLATE_PATH,
            )
        except CertificateRenderStarted.RedirectToPage as exc:
            response = HttpResponseRedirect(exc.redirect_to)
        except CertificateRenderStarted.RenderCustomResponse as exc:
            response = exc.response
        else:
            response = _render_valid_certificate(request, context, custom_template)

        # Render the certificate
        return response
Exemple #14
0
    def _create_or_update(cls, course):
        """
        Creates or updates a CourseOverview object from a CourseDescriptor.

        Does not touch the database, simply constructs and returns an overview
        from the given course.

        Arguments:
            course (CourseDescriptor): any course descriptor object

        Returns:
            CourseOverview: created or updated overview extracted from the given course
        """
        from lms.djangoapps.certificates.api import get_active_web_certificate
        from openedx.core.lib.courses import course_image_url

        # Workaround for a problem discovered in https://openedx.atlassian.net/browse/TNL-2806.
        # If the course has a malformed grading policy such that
        # course._grading_policy['GRADE_CUTOFFS'] = {}, then
        # course.lowest_passing_grade will raise a ValueError.
        # Work around this for now by defaulting to None.
        try:
            lowest_passing_grade = course.lowest_passing_grade
        except ValueError:
            lowest_passing_grade = None

        display_name = course.display_name
        start = course.start
        end = course.end
        max_student_enrollments_allowed = course.max_student_enrollments_allowed
        if isinstance(course.id, CCXLocator):
            from lms.djangoapps.ccx.utils import get_ccx_from_ccx_locator
            ccx = get_ccx_from_ccx_locator(course.id)
            display_name = ccx.display_name
            start = ccx.start
            end = ccx.due
            max_student_enrollments_allowed = ccx.max_student_enrollments_allowed

        course_overview = cls.objects.filter(id=course.id)
        if course_overview.exists():
            log.info(u'Updating course overview for %s.',
                     six.text_type(course.id))
            course_overview = course_overview.first()
        else:
            log.info(u'Creating course overview for %s.',
                     six.text_type(course.id))
            course_overview = cls()

        course_overview.version = cls.VERSION
        course_overview.id = course.id
        course_overview._location = course.location
        course_overview.org = course.location.org
        course_overview.display_name = display_name
        course_overview.display_number_with_default = course.display_number_with_default
        course_overview.display_org_with_default = course.display_org_with_default

        course_overview.start = start
        course_overview.end = end
        course_overview.advertised_start = course.advertised_start
        course_overview.announcement = course.announcement

        course_overview.course_image_url = course_image_url(course)
        course_overview.social_sharing_url = course.social_sharing_url

        course_overview.certificates_display_behavior = course.certificates_display_behavior
        course_overview.certificates_show_before_end = course.certificates_show_before_end
        course_overview.cert_html_view_enabled = course.cert_html_view_enabled
        course_overview.has_any_active_web_certificate = (
            get_active_web_certificate(course) is not None)
        course_overview.cert_name_short = course.cert_name_short
        course_overview.cert_name_long = course.cert_name_long
        course_overview.certificate_available_date = course.certificate_available_date
        course_overview.lowest_passing_grade = lowest_passing_grade
        course_overview.end_of_course_survey_url = course.end_of_course_survey_url

        course_overview.days_early_for_beta = course.days_early_for_beta
        course_overview.mobile_available = course.mobile_available
        course_overview.visible_to_staff_only = course.visible_to_staff_only
        course_overview._pre_requisite_courses_json = json.dumps(
            course.pre_requisite_courses)

        course_overview.enrollment_start = course.enrollment_start
        course_overview.enrollment_end = course.enrollment_end
        course_overview.enrollment_domain = course.enrollment_domain
        course_overview.invitation_only = course.invitation_only
        course_overview.max_student_enrollments_allowed = max_student_enrollments_allowed

        course_overview.catalog_visibility = course.catalog_visibility
        course_overview.short_description = CourseDetails.fetch_about_attribute(
            course.id, 'short_description')
        course_overview.effort = CourseDetails.fetch_about_attribute(
            course.id, 'effort')
        course_overview.course_video_url = CourseDetails.fetch_video_url(
            course.id)
        course_overview.self_paced = course.self_paced

        if not CatalogIntegration.is_enabled():
            course_overview.language = course.language

        return course_overview
Exemple #15
0
    def _create_or_update(cls, course):  # lint-amnesty, pylint: disable=too-many-statements
        """
        Creates or updates a CourseOverview object from a CourseBlock.

        Does not touch the database, simply constructs and returns an overview
        from the given course.

        Arguments:
            course (CourseBlock): any course descriptor object

        Returns:
            CourseOverview: created or updated overview extracted from the given course
        """
        from lms.djangoapps.certificates.api import get_active_web_certificate
        from openedx.core.lib.courses import course_image_url

        # Workaround for a problem discovered in https://openedx.atlassian.net/browse/TNL-2806.
        # If the course has a malformed grading policy such that
        # course._grading_policy['GRADE_CUTOFFS'] = {}, then
        # course.lowest_passing_grade will raise a ValueError.
        # Work around this for now by defaulting to None.
        try:
            lowest_passing_grade = course.lowest_passing_grade
        except ValueError:
            lowest_passing_grade = None

        display_name = course.display_name
        start = course.start
        end = course.end
        max_student_enrollments_allowed = course.max_student_enrollments_allowed
        if isinstance(course.id, CCXLocator):
            from lms.djangoapps.ccx.utils import get_ccx_from_ccx_locator
            ccx = get_ccx_from_ccx_locator(course.id)
            display_name = ccx.display_name
            start = ccx.start
            end = ccx.due
            max_student_enrollments_allowed = ccx.max_student_enrollments_allowed

        course_overview = cls.objects.filter(id=course.id)
        if course_overview.exists():
            log.info('Updating course overview for %s.', str(course.id))
            course_overview = course_overview.first()
            # MySQL ignores casing, but CourseKey doesn't. To prevent multiple
            # courses with different cased keys from overriding each other, we'll
            # check for equality here in python.
            if course_overview.id != course.id:
                raise CourseOverviewCaseMismatchException(
                    course_overview.id, course.id)
        else:
            log.info('Creating course overview for %s.', str(course.id))
            course_overview = cls()

        course_overview.version = cls.VERSION
        course_overview.id = course.id
        course_overview._location = course.location  # lint-amnesty, pylint: disable=protected-access
        course_overview.org = course.location.org
        course_overview.display_name = display_name
        course_overview.display_number_with_default = course.display_number_with_default
        course_overview.display_org_with_default = course.display_org_with_default

        course_overview.start = start
        course_overview.end = end
        course_overview.advertised_start = course.advertised_start
        course_overview.announcement = course.announcement

        course_overview.banner_image_url = course_image_url(
            course, 'banner_image')
        course_overview.course_image_url = course_image_url(course)
        course_overview.social_sharing_url = course.social_sharing_url

        updated_available_date, updated_display_behavior = CourseDetails.validate_certificate_settings(
            course.certificate_available_date,
            course.certificates_display_behavior)

        course_overview.certificates_display_behavior = updated_display_behavior
        course_overview.certificate_available_date = updated_available_date
        course_overview.certificates_show_before_end = course.certificates_show_before_end
        course_overview.cert_html_view_enabled = course.cert_html_view_enabled
        course_overview.has_any_active_web_certificate = (
            get_active_web_certificate(course) is not None)
        course_overview.cert_name_short = course.cert_name_short
        course_overview.cert_name_long = course.cert_name_long
        course_overview.lowest_passing_grade = lowest_passing_grade
        course_overview.end_of_course_survey_url = course.end_of_course_survey_url

        course_overview.days_early_for_beta = course.days_early_for_beta
        course_overview.mobile_available = course.mobile_available
        course_overview.visible_to_staff_only = course.visible_to_staff_only
        course_overview._pre_requisite_courses_json = json.dumps(
            course.pre_requisite_courses)  # lint-amnesty, pylint: disable=protected-access

        course_overview.enrollment_start = course.enrollment_start
        course_overview.enrollment_end = course.enrollment_end
        course_overview.enrollment_domain = course.enrollment_domain
        course_overview.invitation_only = course.invitation_only
        course_overview.max_student_enrollments_allowed = max_student_enrollments_allowed

        course_overview.catalog_visibility = course.catalog_visibility
        course_overview.short_description = CourseDetails.fetch_about_attribute(
            course.id, 'short_description')
        course_overview.effort = CourseDetails.fetch_about_attribute(
            course.id, 'effort')
        course_overview.course_video_url = CourseDetails.fetch_video_url(
            course.id)
        course_overview.self_paced = course.self_paced

        course_overview.has_highlights = cls._get_course_has_highlights(course)

        course_overview.enable_proctored_exams = course.enable_proctored_exams
        course_overview.proctoring_provider = course.proctoring_provider
        course_overview.proctoring_escalation_email = course.proctoring_escalation_email
        course_overview.allow_proctoring_opt_out = course.allow_proctoring_opt_out

        course_overview.entrance_exam_enabled = course.entrance_exam_enabled
        # entrance_exam_id defaults to None in the course object, but '' is more reasonable for a string field
        course_overview.entrance_exam_id = course.entrance_exam_id or ''
        # Despite it being a float, the course object defaults to an int. So we will detect that case and update
        # it to be a float like everything else.
        if isinstance(course.entrance_exam_minimum_score_pct, int):
            course_overview.entrance_exam_minimum_score_pct = course.entrance_exam_minimum_score_pct / 100
        else:
            course_overview.entrance_exam_minimum_score_pct = course.entrance_exam_minimum_score_pct

        if not CatalogIntegration.is_enabled():
            course_overview.language = course.language

        return course_overview
Exemple #16
0
    def check_course_overview_against_course(self, course):
        """
        Compares a CourseOverview object against its corresponding
        CourseDescriptor object.

        Specifically, given a course, test that data within the following three
        objects match each other:
         - the CourseDescriptor itself
         - a CourseOverview that was newly constructed from _create_from_course
         - a CourseOverview that was loaded from the MySQL database

        Arguments:
            course (CourseDescriptor): the course to be checked.
        """
        def get_seconds_since_epoch(date_time):
            """
            Returns the number of seconds between the Unix Epoch and the given
                datetime. If the given datetime is None, return None.

            Arguments:
                date_time (datetime): the datetime in question.
            """
            if date_time is None:
                return None
            epoch = datetime.datetime.utcfromtimestamp(0).replace(
                tzinfo=pytz.utc)
            return math.floor((date_time - epoch).total_seconds())

        # Load the CourseOverview from the cache twice. The first load will be a cache miss (because the cache
        # is empty) so the course will be newly created with CourseOverviewDescriptor.create_from_course. The second
        # load will be a cache hit, so the course will be loaded from the cache.
        course_overview_cache_miss = CourseOverview.get_from_id(course.id)
        course_overview_cache_hit = CourseOverview.get_from_id(course.id)

        # Test if value of these attributes match between the three objects
        fields_to_test = [
            'id',
            'display_name',
            'display_number_with_default',
            'display_org_with_default',
            'advertised_start',
            'facebook_url',
            'social_sharing_url',
            'certificates_display_behavior',
            'certificates_show_before_end',
            'cert_name_short',
            'cert_name_long',
            'lowest_passing_grade',
            'end_of_course_survey_url',
            'mobile_available',
            'visible_to_staff_only',
            'location',
            'number',
            'url_name',
            'display_name_with_default',
            'start_date_is_still_default',
            'pre_requisite_courses',
        ]
        for attribute_name in fields_to_test:
            course_value = getattr(course, attribute_name)
            cache_miss_value = getattr(course_overview_cache_miss,
                                       attribute_name)
            cache_hit_value = getattr(course_overview_cache_hit,
                                      attribute_name)
            self.assertEqual(course_value, cache_miss_value)
            self.assertEqual(cache_miss_value, cache_hit_value)

        # Test if return values for all methods are equal between the three objects
        methods_to_test = [
            ('clean_id', ()),
            ('clean_id', ('#', )),
            ('has_ended', ()),
            ('has_started', ()),
            ('start_datetime_text', ('SHORT_DATE', )),
            ('start_datetime_text', ('DATE_TIME', )),
            ('end_datetime_text', ('SHORT_DATE', )),
            ('end_datetime_text', ('DATE_TIME', )),
            ('may_certify', ()),
        ]
        for method_name, method_args in methods_to_test:
            course_value = getattr(course, method_name)(*method_args)
            cache_miss_value = getattr(course_overview_cache_miss,
                                       method_name)(*method_args)
            cache_hit_value = getattr(course_overview_cache_hit,
                                      method_name)(*method_args)
            self.assertEqual(course_value, cache_miss_value)
            self.assertEqual(cache_miss_value, cache_hit_value)

        # Other values to test
        # Note: we test the start and end attributes here instead of in
        # fields_to_test, because I ran into trouble while testing datetimes
        # for equality. When writing and reading dates from databases, the
        # resulting values are often off by fractions of a second. So, as a
        # workaround, we simply test if the start and end times are the same
        # number of seconds from the Unix epoch.
        others_to_test = [
            (course_image_url(course),
             course_overview_cache_miss.course_image_url,
             course_overview_cache_hit.course_image_url),
            (get_active_web_certificate(course) is not None,
             course_overview_cache_miss.has_any_active_web_certificate,
             course_overview_cache_hit.has_any_active_web_certificate),
            (
                get_seconds_since_epoch(course.start),
                get_seconds_since_epoch(course_overview_cache_miss.start),
                get_seconds_since_epoch(course_overview_cache_hit.start),
            ),
            (
                get_seconds_since_epoch(course.end),
                get_seconds_since_epoch(course_overview_cache_miss.end),
                get_seconds_since_epoch(course_overview_cache_hit.end),
            )
        ]
        for (course_value, cache_miss_value,
             cache_hit_value) in others_to_test:
            self.assertEqual(course_value, cache_miss_value)
            self.assertEqual(cache_miss_value, cache_hit_value)
Exemple #17
0
def render_html_view(request, user_id, course_id):
    """
    This public view generates an HTML representation of the specified user and course
    If a certificate is not available, we display a "Sorry!" screen instead
    """
    try:
        user_id = int(user_id)
    except ValueError:
        raise Http404

    preview_mode = request.GET.get('preview', None)
    platform_name = configuration_helpers.get_value("platform_name", settings.PLATFORM_NAME)
    configuration = CertificateHtmlViewConfiguration.get_config()

    # Kick the user back to the "Invalid" screen if the feature is disabled globally
    if not settings.FEATURES.get('CERTIFICATES_HTML_VIEW', False):
        return _render_invalid_certificate(course_id, platform_name, configuration)

    # Load the course and user objects
    try:
        course_key = CourseKey.from_string(course_id)
        user = User.objects.get(id=user_id)
        course = get_course_by_id(course_key)

    # For any course or user exceptions, kick the user back to the "Invalid" screen
    except (InvalidKeyError, User.DoesNotExist, Http404) as exception:
        error_str = (
            u"Invalid cert: error finding course %s or user with id "
            u"%d. Specific error: %s"
        )
        log.info(error_str, course_id, user_id, str(exception))
        return _render_invalid_certificate(course_id, platform_name, configuration)

    # Kick the user back to the "Invalid" screen if the feature is disabled for the course
    if not course.cert_html_view_enabled:
        log.info(
            u"Invalid cert: HTML certificates disabled for %s. User id: %d",
            course_id,
            user_id,
        )
        return _render_invalid_certificate(course_id, platform_name, configuration)

    # Load user's certificate
    user_certificate = _get_user_certificate(request, user, course_key, course, preview_mode)
    if not user_certificate:
        log.info(
            u"Invalid cert: User %d does not have eligible cert for %s.",
            user_id,
            course_id,
        )
        return _render_invalid_certificate(course_id, platform_name, configuration)

    # Get the active certificate configuration for this course
    # If we do not have an active certificate, we'll need to send the user to the "Invalid" screen
    # Passing in the 'preview' parameter, if specified, will return a configuration, if defined
    active_configuration = get_active_web_certificate(course, preview_mode)
    if active_configuration is None:
        log.info(
            u"Invalid cert: course %s does not have an active configuration. User id: %d",
            course_id,
            user_id,
        )
        return _render_invalid_certificate(course_id, platform_name, configuration)

    # Get data from Discovery service that will be necessary for rendering this Certificate.
    catalog_data = _get_catalog_data_for_course(course_key)

    # Determine whether to use the standard or custom template to render the certificate.
    custom_template = None
    custom_template_language = None
    if settings.FEATURES.get('CUSTOM_CERTIFICATE_TEMPLATES_ENABLED', False):
        log.info(u"Custom certificate for course %s", course_id)
        custom_template, custom_template_language = _get_custom_template_and_language(
            course.id,
            user_certificate.mode,
            catalog_data.pop('content_language', None)
        )

    # Determine the language that should be used to render the certificate.
    # For the standard certificate template, use the user language. For custom templates, use
    # the language associated with the template.
    user_language = translation.get_language()
    certificate_language = custom_template_language if custom_template else user_language

    log.info(
        u"certificate language is: %s for the course: %s",
        certificate_language,
        course_key
    )

    # Generate the certificate context in the correct language, then render the template.
    with translation.override(certificate_language):
        context = {'user_language': user_language}

        _update_context_with_basic_info(context, course_id, platform_name, configuration)

        context['certificate_data'] = active_configuration

        # Append/Override the existing view context values with any mode-specific ConfigurationModel values
        context.update(configuration.get(user_certificate.mode, {}))

        # Append organization info
        _update_organization_context(context, course)

        # Append course info
        _update_course_context(request, context, course, course_key, platform_name)

        # Append course run info from discovery
        context.update(catalog_data)

        # Append user info
        _update_context_with_user_info(context, user, user_certificate)

        # Append social sharing info
        _update_social_context(request, context, course, user, user_certificate, platform_name)

        # Append/Override the existing view context values with certificate specific values
        _update_certificate_context(context, course, user_certificate, platform_name)

        # Append badge info
        _update_badge_context(context, course, user)

        # Append site configuration overrides
        _update_configuration_context(context, configuration)

        # Add certificate header/footer data to current context
        context.update(get_certificate_header_context(is_secure=request.is_secure()))
        context.update(get_certificate_footer_context())

        # Append/Override the existing view context values with any course-specific static values from Advanced Settings
        context.update(course.cert_html_view_overrides)

        # Track certificate view events
        _track_certificate_events(request, context, course, user, user_certificate)

        # Render the certificate
        return _render_valid_certificate(request, context, custom_template)
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.cert_generation_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': {
            'generate_example_certificates': reverse(
                'generate_example_certificates',
                kwargs={'course_id': course.id}
            ),
            '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}
            ),
        }
    }
Exemple #19
0
def render_html_view(request, user_id, course_id):
    """
    This public view generates an HTML representation of the specified user and course
    If a certificate is not available, we display a "Sorry!" screen instead
    """
    try:
        user_id = int(user_id)
    except ValueError:
        raise Http404

    preview_mode = request.GET.get('preview', None)
    platform_name = configuration_helpers.get_value("platform_name",
                                                    settings.PLATFORM_NAME)
    configuration = CertificateHtmlViewConfiguration.get_config()

    # Kick the user back to the "Invalid" screen if the feature is disabled globally
    if not settings.FEATURES.get('CERTIFICATES_HTML_VIEW', False):
        return _render_invalid_certificate(course_id, platform_name,
                                           configuration)

    # Load the course and user objects
    try:
        course_key = CourseKey.from_string(course_id)
        user = User.objects.get(id=user_id)
        course = get_course_by_id(course_key)

    # For any course or user exceptions, kick the user back to the "Invalid" screen
    except (InvalidKeyError, User.DoesNotExist, Http404) as exception:
        error_str = (u"Invalid cert: error finding course %s or user with id "
                     u"%d. Specific error: %s")
        log.info(error_str, course_id, user_id, str(exception))
        return _render_invalid_certificate(course_id, platform_name,
                                           configuration)

    # Kick the user back to the "Invalid" screen if the feature is disabled for the course
    if not course.cert_html_view_enabled:
        log.info(
            u"Invalid cert: HTML certificates disabled for %s. User id: %d",
            course_id,
            user_id,
        )
        return _render_invalid_certificate(course_id, platform_name,
                                           configuration)

    # Load user's certificate
    user_certificate = _get_user_certificate(request, user, course_key, course,
                                             preview_mode)
    if not user_certificate:
        log.info(
            u"Invalid cert: User %d does not have eligible cert for %s.",
            user_id,
            course_id,
        )
        return _render_invalid_certificate(course_id, platform_name,
                                           configuration)

    # Get the active certificate configuration for this course
    # If we do not have an active certificate, we'll need to send the user to the "Invalid" screen
    # Passing in the 'preview' parameter, if specified, will return a configuration, if defined
    active_configuration = get_active_web_certificate(course, preview_mode)
    if active_configuration is None:
        log.info(
            u"Invalid cert: course %s does not have an active configuration. User id: %d",
            course_id,
            user_id,
        )
        return _render_invalid_certificate(course_id, platform_name,
                                           configuration)

    # Get data from Discovery service that will be necessary for rendering this Certificate.
    catalog_data = _get_catalog_data_for_course(course_key)

    # Determine whether to use the standard or custom template to render the certificate.
    custom_template = None
    custom_template_language = None
    if settings.FEATURES.get('CUSTOM_CERTIFICATE_TEMPLATES_ENABLED', False):
        log.info("Custom certificate for course {course_id}".format(
            course_id=course_id))
        custom_template, custom_template_language = _get_custom_template_and_language(
            course.id, user_certificate.mode,
            catalog_data.pop('content_language', None))

    # Determine the language that should be used to render the certificate.
    # For the standard certificate template, use the user language. For custom templates, use
    # the language associated with the template.
    user_language = translation.get_language()
    certificate_language = custom_template_language if custom_template else user_language

    log.info("certificate language is: {language} for the course:{course_key}".
             format(language=certificate_language, course_key=course_key))

    # Generate the certificate context in the correct language, then render the template.
    with translation.override(certificate_language):
        context = {'user_language': user_language}

        _update_context_with_basic_info(context, course_id, platform_name,
                                        configuration)

        context['certificate_data'] = active_configuration

        # Append/Override the existing view context values with any mode-specific ConfigurationModel values
        context.update(configuration.get(user_certificate.mode, {}))

        # Append organization info
        _update_organization_context(context, course)

        # Append course info
        _update_course_context(request, context, course, course_key,
                               platform_name)

        # Append course run info from discovery
        context.update(catalog_data)

        # Append user info
        _update_context_with_user_info(context, user, user_certificate)

        # Append social sharing info
        _update_social_context(request, context, course, user,
                               user_certificate, platform_name)

        # Append/Override the existing view context values with certificate specific values
        _update_certificate_context(context, course, user_certificate,
                                    platform_name)

        # Append badge info
        _update_badge_context(context, course, user)

        # Append site configuration overrides
        _update_configuration_context(context, configuration)

        # Add certificate header/footer data to current context
        context.update(
            get_certificate_header_context(is_secure=request.is_secure()))
        context.update(get_certificate_footer_context())

        # Append/Override the existing view context values with any course-specific static values from Advanced Settings
        context.update(course.cert_html_view_overrides)

        # Track certificate view events
        _track_certificate_events(request, context, course, user,
                                  user_certificate)

        # Render the certificate
        return _render_valid_certificate(request, context, custom_template)
Exemple #20
0
    def _create_from_course(cls, course):
        """
        Creates a CourseOverview object from a CourseDescriptor.

        Does not touch the database, simply constructs and returns an overview
        from the given course.

        Arguments:
            course (CourseDescriptor): any course descriptor object

        Returns:
            CourseOverview: overview extracted from the given course
        """
        from lms.djangoapps.certificates.api import get_active_web_certificate
        from openedx.core.lib.courses import course_image_url

        log.info('Creating course overview for %s.', unicode(course.id))

        # Workaround for a problem discovered in https://openedx.atlassian.net/browse/TNL-2806.
        # If the course has a malformed grading policy such that
        # course._grading_policy['GRADE_CUTOFFS'] = {}, then
        # course.lowest_passing_grade will raise a ValueError.
        # Work around this for now by defaulting to None.
        try:
            lowest_passing_grade = course.lowest_passing_grade
        except ValueError:
            lowest_passing_grade = None

        display_name = course.display_name
        start = course.start
        end = course.end
        max_student_enrollments_allowed = course.max_student_enrollments_allowed
        if isinstance(course.id, CCXLocator):
            from lms.djangoapps.ccx.utils import get_ccx_from_ccx_locator
            ccx = get_ccx_from_ccx_locator(course.id)
            display_name = ccx.display_name
            start = ccx.start
            end = ccx.due
            max_student_enrollments_allowed = ccx.max_student_enrollments_allowed

        return cls(
            version=cls.VERSION,
            id=course.id,
            _location=course.location,
            org=course.location.org,
            display_name=display_name,
            display_number_with_default=course.display_number_with_default,
            display_org_with_default=course.display_org_with_default,

            start=start,
            end=end,
            advertised_start=course.advertised_start,
            announcement=course.announcement,

            course_image_url=course_image_url(course),
            social_sharing_url=course.social_sharing_url,

            certificates_display_behavior=course.certificates_display_behavior,
            certificates_show_before_end=course.certificates_show_before_end,
            cert_html_view_enabled=course.cert_html_view_enabled,
            has_any_active_web_certificate=(get_active_web_certificate(course) is not None),
            cert_name_short=course.cert_name_short,
            cert_name_long=course.cert_name_long,
            lowest_passing_grade=lowest_passing_grade,
            end_of_course_survey_url=course.end_of_course_survey_url,

            days_early_for_beta=course.days_early_for_beta,
            mobile_available=course.mobile_available,
            visible_to_staff_only=course.visible_to_staff_only,
            _pre_requisite_courses_json=json.dumps(course.pre_requisite_courses),

            enrollment_start=course.enrollment_start,
            enrollment_end=course.enrollment_end,
            enrollment_domain=course.enrollment_domain,
            invitation_only=course.invitation_only,
            max_student_enrollments_allowed=max_student_enrollments_allowed,

            catalog_visibility=course.catalog_visibility,
            short_description=CourseDetails.fetch_about_attribute(course.id, 'short_description'),
            effort=CourseDetails.fetch_about_attribute(course.id, 'effort'),
            course_video_url=CourseDetails.fetch_video_url(course.id),
            self_paced=course.self_paced,
        )
    def _create_from_course(cls, course):
        """
        Creates a CourseOverview object from a CourseDescriptor.

        Does not touch the database, simply constructs and returns an overview
        from the given course.

        Arguments:
            course (CourseDescriptor): any course descriptor object

        Returns:
            CourseOverview: overview extracted from the given course
        """
        from lms.djangoapps.certificates.api import get_active_web_certificate
        from openedx.core.lib.courses import course_image_url

        log.info('Creating course overview for %s.', unicode(course.id))

        # Workaround for a problem discovered in https://openedx.atlassian.net/browse/TNL-2806.
        # If the course has a malformed grading policy such that
        # course._grading_policy['GRADE_CUTOFFS'] = {}, then
        # course.lowest_passing_grade will raise a ValueError.
        # Work around this for now by defaulting to None.
        try:
            lowest_passing_grade = course.lowest_passing_grade
        except ValueError:
            lowest_passing_grade = None

        display_name = course.display_name
        start = course.start
        end = course.end
        max_student_enrollments_allowed = course.max_student_enrollments_allowed
        if isinstance(course.id, CCXLocator):
            from lms.djangoapps.ccx.utils import get_ccx_from_ccx_locator
            ccx = get_ccx_from_ccx_locator(course.id)
            display_name = ccx.display_name
            start = ccx.start
            end = ccx.due
            max_student_enrollments_allowed = ccx.max_student_enrollments_allowed

        return cls(
            version=cls.VERSION,
            id=course.id,
            _location=course.location,
            org=course.location.org,
            display_name=display_name,
            display_number_with_default=course.display_number_with_default,
            display_org_with_default=course.display_org_with_default,
            start=start,
            end=end,
            advertised_start=course.advertised_start,
            announcement=course.announcement,
            course_image_url=course_image_url(course),
            social_sharing_url=course.social_sharing_url,
            certificates_display_behavior=course.certificates_display_behavior,
            certificates_show_before_end=course.certificates_show_before_end,
            cert_html_view_enabled=course.cert_html_view_enabled,
            has_any_active_web_certificate=(get_active_web_certificate(course)
                                            is not None),
            cert_name_short=course.cert_name_short,
            cert_name_long=course.cert_name_long,
            lowest_passing_grade=lowest_passing_grade,
            end_of_course_survey_url=course.end_of_course_survey_url,
            days_early_for_beta=course.days_early_for_beta,
            mobile_available=course.mobile_available,
            visible_to_staff_only=course.visible_to_staff_only,
            _pre_requisite_courses_json=json.dumps(
                course.pre_requisite_courses),
            enrollment_start=course.enrollment_start,
            enrollment_end=course.enrollment_end,
            enrollment_domain=course.enrollment_domain,
            invitation_only=course.invitation_only,
            max_student_enrollments_allowed=max_student_enrollments_allowed,
            catalog_visibility=course.catalog_visibility,
            short_description=CourseDetails.fetch_about_attribute(
                course.id, 'short_description'),
            effort=CourseDetails.fetch_about_attribute(course.id, 'effort'),
            course_video_url=CourseDetails.fetch_video_url(course.id),
            self_paced=course.self_paced,
        )