def test_available_vs_display_date(self, feature_enabled, is_self_paced,
                                       uses_avail_date):
        self.course.self_paced = is_self_paced
        with configure_waffle_namespace(feature_enabled):

            # With no available_date set, both return modified_date
            assert self.certificate.modified_date == api.available_date_for_certificate(
                self.course, self.certificate)
            assert self.certificate.modified_date == api.display_date_for_certificate(
                self.course, self.certificate)

            # With an available date set in the past, both return the available date (if configured)
            self.course.certificate_available_date = datetime(2017,
                                                              2,
                                                              1,
                                                              tzinfo=pytz.UTC)
            maybe_avail = self.course.certificate_available_date if uses_avail_date else self.certificate.modified_date
            assert maybe_avail == api.available_date_for_certificate(
                self.course, self.certificate)
            assert maybe_avail == api.display_date_for_certificate(
                self.course, self.certificate)

            # With a future available date, they each return a different date
            self.course.certificate_available_date = datetime.max.replace(
                tzinfo=pytz.UTC)
            maybe_avail = self.course.certificate_available_date if uses_avail_date else self.certificate.modified_date
            assert maybe_avail == api.available_date_for_certificate(
                self.course, self.certificate)
            assert self.certificate.modified_date == api.display_date_for_certificate(
                self.course, self.certificate)
Exemple #2
0
def _update_certificate_context(context, user_certificate, platform_name):
    """
    Build up the certificate web view context using the provided values
    (Helper method to keep the view clean)
    """
    # Populate dynamic output values using the course/certificate data loaded above
    certificate_type = context.get('certificate_type')

    # Override the defaults with any mode-specific static values
    context['certificate_id_number'] = user_certificate.verify_uuid
    context['certificate_verify_url'] = "{prefix}{uuid}{suffix}".format(
        prefix=context.get('certificate_verify_url_prefix'),
        uuid=user_certificate.verify_uuid,
        suffix=context.get('certificate_verify_url_suffix'))

    # Translators:  The format of the date includes the full name of the month
    course = get_course_by_id(
        user_certificate.course_id) if user_certificate.course_id else None
    date = display_date_for_certificate(course, user_certificate)
    context['certificate_date_issued'] = _('{month} {day}, {year}').format(
        month=strftime_localized(date, "%B"), day=date.day, year=date.year)

    # Translators:  This text represents the verification of the certificate
    context['document_meta_description'] = _(
        'This is a valid {platform_name} certificate for {user_name}, '
        'who participated in {partner_short_name} {course_number}').format(
            platform_name=platform_name,
            user_name=context['accomplishment_copy_name'],
            partner_short_name=context['organization_short_name'],
            course_number=context['course_number'])

    # Translators:  This text is bound to the HTML 'title' element of the page and appears in the browser title bar
    context['document_title'] = _(
        "{partner_short_name} {course_number} Certificate | {platform_name}"
    ).format(partner_short_name=context['organization_short_name'],
             course_number=context['course_number'],
             platform_name=platform_name)

    # Translators:  This text fragment appears after the student's name (displayed in a large font) on the certificate
    # screen.  The text describes the accomplishment represented by the certificate information displayed to the user
    context['accomplishment_copy_description_full'] = _(
        "successfully completed, received a passing grade, and was "
        "awarded this {platform_name} {certificate_type} "
        "Certificate of Completion in ").format(
            platform_name=platform_name,
            certificate_type=context.get("certificate_type"))

    certificate_type_description = get_certificate_description(
        user_certificate.mode, certificate_type, platform_name)
    if certificate_type_description:
        context['certificate_type_description'] = certificate_type_description

    # Translators: This text describes the purpose (and therefore, value) of a course certificate
    context['certificate_info_description'] = _(
        "{platform_name} acknowledges achievements through "
        "certificates, which are awarded for course activities "
        "that {platform_name} students complete.").format(
            platform_name=platform_name,
            tos_url=context.get('company_tos_url'),
            verified_cert_url=context.get('company_verified_certificate_url'))
Exemple #3
0
def _update_certificate_context(context, course, user_certificate, platform_name):
    """
    Build up the certificate web view context using the provided values
    (Helper method to keep the view clean)
    """
    # Populate dynamic output values using the course/certificate data loaded above
    certificate_type = context.get('certificate_type')

    # Override the defaults with any mode-specific static values
    context['certificate_id_number'] = user_certificate.verify_uuid
    context['certificate_verify_url'] = "{prefix}{uuid}{suffix}".format(
        prefix=context.get('certificate_verify_url_prefix'),
        uuid=user_certificate.verify_uuid,
        suffix=context.get('certificate_verify_url_suffix')
    )

    # Translators:  The format of the date includes the full name of the month
    date = display_date_for_certificate(course, user_certificate)
    context['certificate_date_issued'] = _('{month} {day}, {year}').format(
        month=strftime_localized(date, "%B"),
        day=date.day,
        year=date.year
    )

    # Translators:  This text represents the verification of the certificate
    context['document_meta_description'] = _('This is a valid {platform_name} certificate for {user_name}, '
                                             'who participated in {partner_short_name} {course_number}').format(
        platform_name=platform_name,
        user_name=context['accomplishment_copy_name'],
        partner_short_name=context['organization_short_name'],
        course_number=context['course_number']
    )

    # Translators:  This text is bound to the HTML 'title' element of the page and appears in the browser title bar
    context['document_title'] = _("{partner_short_name} {course_number} Certificate | {platform_name}").format(
        partner_short_name=context['organization_short_name'],
        course_number=context['course_number'],
        platform_name=platform_name
    )

    # Translators:  This text fragment appears after the student's name (displayed in a large font) on the certificate
    # screen.  The text describes the accomplishment represented by the certificate information displayed to the user
    context['accomplishment_copy_description_full'] = _("successfully completed, received a passing grade, and was "
                                                        "awarded this {platform_name} {certificate_type} "
                                                        "Certificate of Completion in ").format(
        platform_name=platform_name,
        certificate_type=context.get("certificate_type"))

    certificate_type_description = get_certificate_description(user_certificate.mode, certificate_type, platform_name)
    if certificate_type_description:
        context['certificate_type_description'] = certificate_type_description

    # Translators: This text describes the purpose (and therefore, value) of a course certificate
    context['certificate_info_description'] = _("{platform_name} acknowledges achievements through "
                                                "certificates, which are awarded for course activities "
                                                "that {platform_name} students complete.").format(
        platform_name=platform_name,
        tos_url=context.get('company_tos_url'),
        verified_cert_url=context.get('company_verified_certificate_url'))
    def test_available_vs_display_date(
            self, feature_enabled, is_self_paced, uses_avail_date
    ):
        self.course.self_paced = is_self_paced
        with configure_waffle_namespace(feature_enabled):

            # With no available_date set, both return modified_date
            self.assertEqual(self.certificate.modified_date, api.available_date_for_certificate(self.course, self.certificate))
            self.assertEqual(self.certificate.modified_date, api.display_date_for_certificate(self.course, self.certificate))

            # With an available date set in the past, both return the available date (if configured)
            self.course.certificate_available_date = datetime(2017, 2, 1, tzinfo=pytz.UTC)
            maybe_avail = self.course.certificate_available_date if uses_avail_date else self.certificate.modified_date
            self.assertEqual(maybe_avail, api.available_date_for_certificate(self.course, self.certificate))
            self.assertEqual(maybe_avail, api.display_date_for_certificate(self.course, self.certificate))

            # With a future available date, they each return a different date
            self.course.certificate_available_date = datetime.max.replace(tzinfo=pytz.UTC)
            maybe_avail = self.course.certificate_available_date if uses_avail_date else self.certificate.modified_date
            self.assertEqual(maybe_avail, api.available_date_for_certificate(self.course, self.certificate))
            self.assertEqual(self.certificate.modified_date, api.display_date_for_certificate(self.course, self.certificate))
Exemple #5
0
def get_arabic_certificate_extra_context(request, course_id):
    """
    Compute `date_issued` and `gender` for the Arabic Course Certificates.

    This method is used directly from arabic certificate template. This is done to avoid core changes and customise the
    Arabic certificates. To display gender specific text, we need to send gender in context. Date formatting is also
    custom for Arabic certificates so this method returns date in the required format as well.

    Args:
        request (WSGIRequest): request object
        course_id (str): Course Id

    Returns:
        tuple: Gender of the learner and Date issued.
    """
    certificate = None
    certificate_uuid = request.resolver_match.kwargs.get('certificate_uuid', None)
    if certificate_uuid:
        certificate = GeneratedCertificate.eligible_certificates.filter(
            verify_uuid=certificate_uuid,
            status=CertificateStatuses.downloadable
        ).first()

    user = certificate.user if certificate else request.user
    course_key = CourseKey.from_string(course_id)
    course = get_course_by_id(course_key)

    if not certificate:
        preview_mode = request.GET.get('preview', None)
        certificate = _get_user_certificate(request, user, course_key, course, preview_mode)

    date_format = u'{day} {month} {year}' if course.language == ARABIC_LANGUAGE_CODE else u'{month} {day}, {year}'
    display_date = display_date_for_certificate(course, certificate)

    with translation.override(ARABIC_LANGUAGE_CODE):
        date_issued = _(date_format).format(  # pylint: disable=translation-of-non-string
            month=strftime_localized(display_date, '%B'),
            day=display_date.day,
            year=display_date.year
        )

    return user.profile.gender, date_issued
Exemple #6
0
    def get_certificates(self):
        """
        Get certificates data for user.

        Returns:
            list: Contains dicts which has data for all the courses.
        """
        user = self.request.user
        certificates = GeneratedCertificate.eligible_certificates.filter(
            user=user).order_by('course_id')

        certificate_data = []
        for cert in certificates:
            formatted_cert = format_certificate_for_user(user.username, cert)

            if formatted_cert:
                course = get_course_by_id(cert.course_id)
                formatted_cert['course_display_name'] = course.display_name
                formatted_cert['date_created'] = strftime_localized(
                    display_date_for_certificate(course, cert), '%b %d, %Y')

                certificate_data.append(formatted_cert)

        return certificate_data
Exemple #7
0
def award_course_certificate(self, username, course_run_key):
    """
    This task is designed to be called whenever a student GeneratedCertificate is updated.
    It can be called independently for a username and a course_run, but is invoked on each GeneratedCertificate.save.
    """
    LOGGER.info('Running task award_course_certificate for username %s',
                username)

    countdown = 2**self.request.retries

    # If the credentials config model is disabled for this
    # feature, it may indicate a condition where processing of such tasks
    # has been temporarily disabled.  Since this is a recoverable situation,
    # mark this task for retry instead of failing it altogether.

    if not CredentialsApiConfig.current().is_learner_issuance_enabled:
        LOGGER.warning(
            'Task award_course_certificate cannot be executed when credentials issuance is disabled in API config',
        )
        raise self.retry(countdown=countdown, max_retries=MAX_RETRIES)

    try:
        course_key = CourseKey.from_string(course_run_key)
        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            LOGGER.exception(
                'Task award_course_certificate was called with invalid username %s',
                username)
            # Don't retry for this case - just conclude the task.
            return
        # Get the cert for the course key and username if it's both passing and available in professional/verified
        try:
            certificate = GeneratedCertificate.eligible_certificates.get(
                user=user.id, course_id=course_key)
        except GeneratedCertificate.DoesNotExist:
            LOGGER.exception(
                'Task award_course_certificate was called without Certificate found for %s to user %s',
                course_key, username)
            return
        if certificate.mode in CourseMode.VERIFIED_MODES + CourseMode.CREDIT_MODES:
            try:
                course_overview = CourseOverview.get_from_id(course_key)
            except (CourseOverview.DoesNotExist, IOError):
                LOGGER.exception(
                    'Task award_course_certificate was called without course overview data for course %s',
                    course_key)
                return
            credentials_client = get_credentials_api_client(
                User.objects.get(
                    username=settings.CREDENTIALS_SERVICE_USERNAME),
                org=course_key.org,
            )
            # FIXME This may result in visible dates that do not update alongside the Course Overview if that changes
            # This is a known limitation of this implementation and was chosen to reduce the amount of replication,
            # endpoints, celery tasks, and jenkins jobs that needed to be written for this functionality
            visible_date = display_date_for_certificate(
                course_overview, certificate)
            post_course_certificate(credentials_client, username, certificate,
                                    visible_date)

            LOGGER.info('Awarded certificate for course %s to user %s',
                        course_key, username)
    except Exception as exc:
        LOGGER.exception(
            'Failed to determine course certificates to be awarded for user %s',
            username)
        raise self.retry(exc=exc, countdown=countdown, max_retries=MAX_RETRIES)
Exemple #8
0
def award_course_certificate(self, username, course_run_key):
    """
    This task is designed to be called whenever a student GeneratedCertificate is updated.
    It can be called independently for a username and a course_run, but is invoked on each GeneratedCertificate.save.
    """
    LOGGER.info('Running task award_course_certificate for username %s', username)

    countdown = 2 ** self.request.retries

    # If the credentials config model is disabled for this
    # feature, it may indicate a condition where processing of such tasks
    # has been temporarily disabled.  Since this is a recoverable situation,
    # mark this task for retry instead of failing it altogether.

    if not CredentialsApiConfig.current().is_learner_issuance_enabled:
        LOGGER.warning(
            'Task award_course_certificate cannot be executed when credentials issuance is disabled in API config',
        )
        raise self.retry(countdown=countdown, max_retries=MAX_RETRIES)

    try:
        course_key = CourseKey.from_string(course_run_key)
        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            LOGGER.exception('Task award_course_certificate was called with invalid username %s', username)
            # Don't retry for this case - just conclude the task.
            return
        # Get the cert for the course key and username if it's both passing and available in professional/verified
        try:
            certificate = GeneratedCertificate.eligible_certificates.get(
                user=user.id,
                course_id=course_key
            )
        except GeneratedCertificate.DoesNotExist:
            LOGGER.exception(
                'Task award_course_certificate was called without Certificate found for %s to user %s',
                course_key,
                username
            )
            return
        if certificate.mode in CourseMode.VERIFIED_MODES + CourseMode.CREDIT_MODES:
            try:
                course_overview = CourseOverview.get_from_id(course_key)
            except (CourseOverview.DoesNotExist, IOError):
                LOGGER.exception(
                    'Task award_course_certificate was called without course overview data for course %s',
                    course_key
                )
                return
            credentials_client = get_credentials_api_client(User.objects.get(
                username=settings.CREDENTIALS_SERVICE_USERNAME),
                org=course_key.org,
            )
            # FIXME This may result in visible dates that do not update alongside the Course Overview if that changes
            # This is a known limitation of this implementation and was chosen to reduce the amount of replication,
            # endpoints, celery tasks, and jenkins jobs that needed to be written for this functionality
            visible_date = display_date_for_certificate(course_overview, certificate)
            post_course_certificate(credentials_client, username, certificate, visible_date)

            LOGGER.info('Awarded certificate for course %s to user %s', course_key, username)
    except Exception as exc:
        LOGGER.exception('Failed to determine course certificates to be awarded for user %s', username)
        raise self.retry(exc=exc, countdown=countdown, max_retries=MAX_RETRIES)