Exemple #1
0
    def test_refundable_when_certificate_exists(self, cutoff_date):
        """ Assert that enrollment is not refundable once a certificat has been generated."""

        cutoff_date.return_value = datetime.now(pytz.UTC) + timedelta(days=1)

        self.assertTrue(self.enrollment.refundable())

        GeneratedCertificateFactory.create(
            user=self.user,
            course_id=self.course.id,
            status=CertificateStatuses.downloadable,
            mode='verified'
        )

        self.assertFalse(self.enrollment.refundable())
        self.assertFalse(
            self.enrollment.refundable(
                user_already_has_certs_for=GeneratedCertificate.course_ids_with_certs_for_user(self.user)
            )
        )

        # Assert that can_refund overrides this and allows refund
        self.enrollment.can_refund = True
        self.assertTrue(self.enrollment.refundable())
        self.assertTrue(
            self.enrollment.refundable(
                user_already_has_certs_for=GeneratedCertificate.course_ids_with_certs_for_user(self.user)
            )
        )
Exemple #2
0
    def test_course_ids_with_certs_for_user(self):
        # Create one user with certs and one without
        student_no_certs = UserFactory()
        student_with_certs = UserFactory()
        student_with_certs.profile.allow_certificate = True
        student_with_certs.profile.save()

        # Set up a couple of courses
        course_1 = CourseFactory.create()
        course_2 = CourseFactory.create()

        # Generate certificates
        GeneratedCertificateFactory.create(
            user=student_with_certs,
            course_id=course_1.id,
            status=CertificateStatuses.downloadable,
            mode='honor'
        )
        GeneratedCertificateFactory.create(
            user=student_with_certs,
            course_id=course_2.id,
            status=CertificateStatuses.downloadable,
            mode='honor'
        )

        # User with no certs should return an empty set.
        self.assertSetEqual(
            GeneratedCertificate.course_ids_with_certs_for_user(student_no_certs),
            set()
        )
        # User with certs should return a set with the two course_ids
        self.assertSetEqual(
            GeneratedCertificate.course_ids_with_certs_for_user(student_with_certs),
            {course_1.id, course_2.id}
        )
Exemple #3
0
    def test_course_ids_with_certs_for_user(self):
        # Create one user with certs and one without
        student_no_certs = UserFactory()
        student_with_certs = UserFactory()
        student_with_certs.profile.allow_certificate = True
        student_with_certs.profile.save()

        # Set up a couple of courses
        course_1 = CourseFactory.create()
        course_2 = CourseFactory.create()

        # Generate certificates
        GeneratedCertificateFactory.create(
            user=student_with_certs,
            course_id=course_1.id,
            status=CertificateStatuses.downloadable,
            mode='honor')
        GeneratedCertificateFactory.create(
            user=student_with_certs,
            course_id=course_2.id,
            status=CertificateStatuses.downloadable,
            mode='honor')

        # User with no certs should return an empty set.
        self.assertSetEqual(
            GeneratedCertificate.course_ids_with_certs_for_user(
                student_no_certs), set())
        # User with certs should return a set with the two course_ids
        self.assertSetEqual(
            GeneratedCertificate.course_ids_with_certs_for_user(
                student_with_certs), {course_1.id, course_2.id})
Exemple #4
0
def _get_user_certificate(request,
                          user,
                          course_key,
                          course,
                          preview_mode=None):
    """
    Retrieves user's certificate from db. Creates one in case of preview mode.
    Returns None if there is no certificate generated for given user
    otherwise returns `GeneratedCertificate` instance.
    """
    try:
        # Attempt to load the user's generated certificate data
        if preview_mode:
            user_certificate = GeneratedCertificate.objects.get(
                user=user, course_id=course_key, mode=preview_mode)
        else:
            user_certificate = GeneratedCertificate.objects.get(
                user=user,
                course_id=course_key,
                status=CertificateStatuses.downloadable)

    # If there's no generated certificate data for this user, we need to see if we're in 'preview' mode...
    # If we are, we'll need to create a mock version of the user_certificate container for previewing
    except GeneratedCertificate.DoesNotExist:
        if preview_mode and (has_access(request.user, 'instructor', course)
                             or has_access(request.user, 'staff', course)):
            user_certificate = GeneratedCertificate(
                mode=preview_mode,
                verify_uuid=unicode(uuid4().hex),
                modified_date=datetime.now().date())
        else:
            return None

    return user_certificate
Exemple #5
0
def _listen_for_passing_grade(sender, user, course_id, **kwargs):  # pylint: disable=unused-argument
    """
    Listen for a learner passing a course, send cert generation task,
    downstream signal from COURSE_GRADE_CHANGED
    """

    # No flags enabled
    if (
        not waffle.waffle().is_enabled(waffle.SELF_PACED_ONLY) and
        not waffle.waffle().is_enabled(waffle.INSTRUCTOR_PACED_ONLY)
    ):
        return

    # Only SELF_PACED_ONLY flag enabled
    if waffle.waffle().is_enabled(waffle.SELF_PACED_ONLY):
        if not courses.get_course_by_id(course_key, depth=0).self_paced:
            return

    # Only INSTRUCTOR_PACED_ONLY flag enabled
    elif waffle.waffle().is_enabled(waffle.INSTRUCTOR_PACED_ONLY):
        if courses.get_course_by_id(course_key, depth=0).self_paced:
            return
    if GeneratedCertificate.certificate_for_student(self.user, self.course_id) is None:
        generate_certificate.apply_async(
            student=user,
            course_key=course_id,
        )
        log.info(u'Certificate generation task initiated for {user} : {course} via passing grade'.format(
            user=user.id,
            course=course_id
        ))
Exemple #6
0
def fire_ungenerated_certificate_task(user,
                                      course_key,
                                      expected_verification_status=None):
    """
    Helper function to fire un-generated certificate tasks

    The 'mode_is_verified' query is copied from the GeneratedCertificate model,
    but is done here in an attempt to reduce traffic to the workers.
    If the learner is verified and their cert has the 'unverified' status,
    we regenerate the cert.
    """
    enrollment_mode, __ = CourseEnrollment.enrollment_mode_for_user(
        user, course_key)
    mode_is_verified = enrollment_mode in GeneratedCertificate.VERIFIED_CERTS_MODES
    cert = GeneratedCertificate.certificate_for_student(user, course_key)
    if mode_is_verified and (cert is None or cert.status == 'unverified'):
        kwargs = {
            'student': unicode(user.id),
            'course_key': unicode(course_key)
        }
        if expected_verification_status:
            kwargs['expected_verification_status'] = unicode(
                expected_verification_status)
        generate_certificate.apply_async(countdown=CERTIFICATE_DELAY_SECONDS,
                                         kwargs=kwargs)
        return True
Exemple #7
0
def fire_ungenerated_certificate_task(user, course_id):
    """
    Helper function to fire un-generated certificate tasks
    """
    if GeneratedCertificate.certificate_for_student(user, course_id) is None:
        generate_certificate.apply_async(student=user, course_key=course_id)
        return True
Exemple #8
0
    def refundable(self):
        """
        For paid/verified certificates, students may receive a refund if they have
        a verified certificate and the deadline for refunds has not yet passed.
        """
        # In order to support manual refunds past the deadline, set can_refund on this object.
        # On unenrolling, the "unenroll_done" signal calls CertificateItem.refund_cert_callback(),
        # which calls this method to determine whether to refund the order.
        # This can't be set directly because refunds currently happen as a side-effect of unenrolling.
        # (side-effects are bad)
        if getattr(self, 'can_refund', None) is not None:
            return True

        # If the student has already been given a certificate they should not be refunded
        if GeneratedCertificate.certificate_for_student(
                self.user, self.course_id) is not None:
            return False

        #TODO - When Course administrators to define a refund period for paid courses then refundable will be supported. # pylint: disable=W0511

        course_mode = CourseMode.mode_for_course(self.course_id, 'verified')
        if course_mode is None:
            return False
        else:
            return True
    def test_ineligible_cert_whitelisted(self):
        """Test that audit mode students can receive a certificate if they are whitelisted."""
        # Enroll as audit
        CourseEnrollmentFactory(user=self.user_2,
                                course_id=self.course.id,
                                is_active=True,
                                mode='audit')
        # Whitelist student
        CertificateWhitelistFactory(course_id=self.course.id, user=self.user_2)

        # Generate certs
        with patch('courseware.grades.grade',
                   Mock(return_value={
                       'grade': 'Pass',
                       'percent': 0.75
                   })):
            with patch.object(XQueueInterface, 'send_to_queue') as mock_send:
                mock_send.return_value = (0, None)
                self.xqueue.add_cert(self.user_2, self.course.id)

        # Assert cert generated correctly
        self.assertTrue(mock_send.called)
        certificate = GeneratedCertificate.certificate_for_student(
            self.user_2, self.course.id)
        self.assertIsNotNone(certificate)
        self.assertEqual(certificate.mode, 'audit')
Exemple #10
0
def _get_user_certificate(request,
                          user,
                          course_key,
                          course,
                          preview_mode=None):
    """
    Retrieves user's certificate from db. Creates one in case of preview mode.
    Returns None if there is no certificate generated for given user
    otherwise returns `GeneratedCertificate` instance.
    """
    user_certificate = None
    if preview_mode:
        # certificate is being previewed from studio
        if has_access(request.user, 'instructor', course) or has_access(
                request.user, 'staff', course):
            user_certificate = GeneratedCertificate(
                mode=preview_mode,
                verify_uuid=unicode(uuid4().hex),
                modified_date=datetime.now().date())
    else:
        # certificate is being viewed by learner or public
        try:
            user_certificate = GeneratedCertificate.eligible_certificates.get(
                user=user,
                course_id=course_key,
                status=CertificateStatuses.downloadable)
        except GeneratedCertificate.DoesNotExist:
            pass

    return user_certificate
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.id, 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),
        "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}),
        },
    }
    def setUp(self):
        CourseFactory.create(org='TESTX', number='1', display_name='TEST1',
                             start=datetime.datetime(2010, 5, 12, 2, 42, tzinfo=utc),
                             end=datetime.datetime(2011, 5, 12, 2, 42, tzinfo=utc))
        CourseFactory.create(org='TESTX', number='2', display_name='TEST2',
                             start=datetime.datetime(2010, 5, 12, 2, 42, tzinfo=utc),
                             end=datetime.datetime(2011, 5, 12, 2, 42, tzinfo=utc))
        CourseFactory.create(org='TESTX', number='3', display_name='TEST3',
                             start=datetime.datetime(2010, 5, 12, 2, 42, tzinfo=utc),
                             end=datetime.datetime(2011, 5, 12, 2, 42, tzinfo=utc))

        self.fred = fred = User(username='******', email='*****@*****.**')
        fred.save()
        UserProfile(user=fred, name='Fred Flintstone').save()
        LinkedIn(user=fred, has_linkedin_account=True).save()
        self.barney = barney = User(
            username='******', email='*****@*****.**')
        barney.save()

        LinkedIn(user=barney, has_linkedin_account=True).save()
        UserProfile(user=barney, name='Barney Rubble').save()

        self.adam = adam = User(
            username='******', email='*****@*****.**')
        adam.save()

        LinkedIn(user=adam, has_linkedin_account=True).save()
        UserProfile(user=adam, name='Adam (חיים פּלי)').save()
        self.cert1 = cert1 = GeneratedCertificate(
            status='downloadable',
            user=fred,
            course_id='TESTX/1/TEST1',
            name='TestX/Intro101',
            download_url='http://test.foo/test')
        cert1.save()
        cert2 = GeneratedCertificate(
            status='downloadable',
            user=fred,
            course_id='TESTX/2/TEST2')
        cert2.save()
        cert3 = GeneratedCertificate(
            status='downloadable',
            user=barney,
            course_id='TESTX/3/TEST3')
        cert3.save()
        cert5 = GeneratedCertificate(
            status='downloadable',
            user=adam,
            course_id='TESTX/3/TEST3')
        cert5.save()
Exemple #13
0
 def create(self):
     self.certificate_obj = GeneratedCertificate(
         user_id=self.user_id,
         course_id=self.course_id,
         grade=self.grade,
         status=self.certificate_status
     )
     if self.certificate_status == 'downloadable':
         self.certificate_obj.download_url = self.pdf_url_filename
     else:
         self.certificate_obj.download_url = ''
     self.certificate_obj.save()
Exemple #14
0
def fire_ungenerated_certificate_task(user, course_key):
    """
    Helper function to fire un-generated certificate tasks
    :param user: A User object.
    :param course_id: A CourseKey object.
    """
    if GeneratedCertificate.certificate_for_student(user, course_key) is None:
        generate_certificate.apply_async(kwargs={
            'student': unicode(user.id),
            'course_key': unicode(course_key),
        })
        return True
Exemple #15
0
 def test_no_match(self):
     """
     Make sure a badge isn't created before a user's reached any checkpoint.
     """
     user = UserFactory()
     course = CourseFactory()
     GeneratedCertificate(
         # pylint: disable=no-member
         user=user,
         course_id=course.location.course_key,
         status=CertificateStatuses.downloadable).save()
     # pylint: disable=no-member
     self.assertFalse(user.badgeassertion_set.all())
Exemple #16
0
def fire_ungenerated_certificate_task(user,
                                      course_key,
                                      expected_verification_status=None):
    """
    Helper function to fire certificate generation task.
    Auto-generation of certificates is available for following course modes:
        1- VERIFIED
        2- CREDIT_MODE
        3- PROFESSIONAL
        4- NO_ID_PROFESSIONAL_MODE

    Certificate generation task is fired to either generate a certificate
    when there is no generated certificate for user in a particular course or
    update a certificate if it has 'unverified' status.

    Task is fired to attempt an update to a certificate
    with 'unverified' status as this method is called when a user is
    successfully verified, any certificate associated
    with such user can now be verified.

    NOTE: Purpose of restricting other course modes (HONOR and AUDIT) from auto-generation is to reduce
    traffic to workers.
    """

    allowed_enrollment_modes_list = [
        CourseMode.VERIFIED,
        CourseMode.CREDIT_MODE,
        CourseMode.PROFESSIONAL,
        CourseMode.NO_ID_PROFESSIONAL_MODE,
    ]
    enrollment_mode, __ = CourseEnrollment.enrollment_mode_for_user(
        user, course_key)
    cert = GeneratedCertificate.certificate_for_student(user, course_key)

    generate_learner_certificate = (
        enrollment_mode in allowed_enrollment_modes_list
        and (cert is None or cert.status == 'unverified'))

    if generate_learner_certificate:
        kwargs = {
            'student': unicode(user.id),
            'course_key': unicode(course_key)
        }
        if expected_verification_status:
            kwargs['expected_verification_status'] = unicode(
                expected_verification_status)
        generate_certificate.apply_async(countdown=CERTIFICATE_DELAY_SECONDS,
                                         kwargs=kwargs)
        return True
Exemple #17
0
    def is_entitlement_regainable(self, entitlement):
        """
        Determines from the policy if an entitlement can still be regained by the user, if they choose
        to by leaving and regaining their entitlement within policy.regain_period days from start date of
        the course or their redemption, whichever comes later, and the expiration period hasn't passed yet
        """
        if entitlement.enrollment_course_run:
            if GeneratedCertificate.certificate_for_student(
                    entitlement.user_id, entitlement.enrollment_course_run.course_id) is not None:
                return False

            # This is >= because a days_until_expiration 0 means that the expiration day has not fully passed yet
            # and that the entitlement should not be expired as there is still time
            return self.get_days_until_expiration(entitlement) >= 0
        return False
Exemple #18
0
    def is_entitlement_regainable(self, entitlement):
        """
        Determines from the policy if an entitlement can still be regained by the user, if they choose
        to by leaving and regaining their entitlement within policy.regain_period days from start date of
        the course or their redemption, whichever comes later, and the expiration period hasn't passed yet
        """
        if entitlement.enrollment_course_run:
            if GeneratedCertificate.certificate_for_student(
                    entitlement.user_id,
                    entitlement.enrollment_course_run.course_id) is not None:
                return False

            # This is >= because a days_until_expiration 0 means that the expiration day has not fully passed yet
            # and that the entitlement should not be expired as there is still time
            return self.get_days_until_expiration(entitlement) >= 0
        return False
Exemple #19
0
 def test_checkpoint_matches(self, checkpoint, required_badges):
     """
     Make sure the proper badges are awarded at the right checkpoints.
     """
     user = UserFactory()
     courses = [CourseFactory() for _i in range(required_badges)]
     for course in courses:
         GeneratedCertificate(
             # pylint: disable=no-member
             user=user,
             course_id=course.location.course_key,
             status=CertificateStatuses.downloadable).save()
     # pylint: disable=no-member
     assertions = user.badgeassertion_set.all().order_by('id')
     # pylint: disable=no-member
     self.assertEqual(user.badgeassertion_set.all().count(), checkpoint)
     self.assertEqual(assertions[checkpoint - 1].badge_class,
                      self.badge_classes[checkpoint - 1])
Exemple #20
0
def is_certificate_invalid(student, course_key):
    """Check that whether the student in the course has been invalidated
    for receiving certificates.

    Arguments:
        student (user object): logged-in user
        course_key (CourseKey): The course identifier.

    Returns:
        Boolean denoting whether the student in the course is invalidated
        to receive certificates
    """
    is_invalid = False
    certificate = GeneratedCertificate.certificate_for_student(student, course_key)
    if certificate is not None:
        is_invalid = CertificateInvalidation.has_certificate_invalidation(student, course_key)

    return is_invalid
Exemple #21
0
def is_certificate_invalid(student, course_key):
    """Check that whether the student in the course has been invalidated
    for receiving certificates.

    Arguments:
        student (user object): logged-in user
        course_key (CourseKey): The course identifier.

    Returns:
        Boolean denoting whether the student in the course is invalidated
        to receive certificates
    """
    is_invalid = False
    certificate = GeneratedCertificate.certificate_for_student(student, course_key)
    if certificate is not None:
        is_invalid = CertificateInvalidation.has_certificate_invalidation(student, course_key)

    return is_invalid
Exemple #22
0
def fire_ungenerated_certificate_task(user, course_key):
    """
    Helper function to fire un-generated certificate tasks

    The 'mode_is_verified' query is copied from the GeneratedCertificate model,
    but is done here in an attempt to reduce traffic to the workers.
    If the learner is verified and their cert has the 'unverified' status,
    we regenerate the cert.
    """
    enrollment_mode, __ = CourseEnrollment.enrollment_mode_for_user(user, course_key)
    mode_is_verified = enrollment_mode in GeneratedCertificate.VERIFIED_CERTS_MODES
    cert = GeneratedCertificate.certificate_for_student(user, course_key)
    if mode_is_verified and (cert is None or cert.status == 'unverified'):
        generate_certificate.apply_async(kwargs={
            'student': unicode(user.id),
            'course_key': unicode(course_key),
        })
        return True
Exemple #23
0
    def refundable(self):
        """
        For paid/verified certificates, students may receive a refund if they have
        a verified certificate and the deadline for refunds has not yet passed.
        """
        # In order to support manual refunds past the deadline, set can_refund on this object.
        # On unenrolling, the "unenroll_done" signal calls CertificateItem.refund_cert_callback(),
        # which calls this method to determine whether to refund the order.
        # This can't be set directly because refunds currently happen as a side-effect of unenrolling.
        # (side-effects are bad)
        if getattr(self, 'can_refund', None) is not None:
            return True

        # If the student has already been given a certificate they should not be refunded
        if GeneratedCertificate.certificate_for_student(self.user, self.course_id) is not None:
            return False

        course_mode = CourseMode.mode_for_course(self.course_id, 'verified')
        if course_mode is None:
            return False
        else:
            return True
    def test_ineligible_cert_whitelisted(self):
        """Test that audit mode students can receive a certificate if they are whitelisted."""
        # Enroll as audit
        CourseEnrollmentFactory(
            user=self.user_2,
            course_id=self.course.id,
            is_active=True,
            mode='audit'
        )
        # Whitelist student
        CertificateWhitelistFactory(course_id=self.course.id, user=self.user_2)

        # Generate certs
        with patch('courseware.grades.grade', Mock(return_value={'grade': 'Pass', 'percent': 0.75})):
            with patch.object(XQueueInterface, 'send_to_queue') as mock_send:
                mock_send.return_value = (0, None)
                self.xqueue.add_cert(self.user_2, self.course.id)

        # Assert cert generated correctly
        self.assertTrue(mock_send.called)
        certificate = GeneratedCertificate.certificate_for_student(self.user_2, self.course.id)
        self.assertIsNotNone(certificate)
        self.assertEqual(certificate.mode, 'audit')
Exemple #25
0
 def test_group_matches(self):
     """
     Make sure the proper badges are awarded when groups are completed.
     """
     user = UserFactory()
     items = list(self.config_map.items())
     for badge_class, course_keys in items:
         for i, key in enumerate(course_keys):
             GeneratedCertificate(
                 user=user,
                 course_id=key,
                 status=CertificateStatuses.downloadable).save()
             # We don't award badges until all three are set.
             if i + 1 == len(course_keys):
                 self.assertTrue(badge_class.get_for_user(user))
             else:
                 self.assertFalse(badge_class.get_for_user(user))
     # pylint: disable=no-member
     classes = [
         badge.badge_class.id for badge in user.badgeassertion_set.all()
     ]
     source_classes = [badge.id for badge in self.badge_classes]
     self.assertEqual(classes, source_classes)
Exemple #26
0
def render_html_view(request, user_id, course_id):
    """
    This public view generates an HTML representation of the specified student's certificate
    If a certificate is not available, we display a "Sorry!" screen instead
    """

    # Create the initial view context, bootstrapping with Django settings and passed-in values
    context = {}
    context['platform_name'] = microsite.get_value("platform_name",
                                                   settings.PLATFORM_NAME)
    context['course_id'] = course_id

    # Update the view context with the default ConfigurationModel settings
    configuration = CertificateHtmlViewConfiguration.get_config()
    # if we are in a microsite, then let's first see if there is an override
    # section in our config
    config_key = microsite.get_value('microsite_config_key', 'default')
    # if there is no special microsite override, then let's use default
    if config_key not in configuration:
        config_key = 'default'
    context.update(configuration.get(config_key, {}))

    # Translators:  'All rights reserved' is a legal term used in copyrighting to protect published content
    reserved = _("All rights reserved")
    context[
        'copyright_text'] = '© {year} {platform_name}. {reserved}.'.format(
            year=settings.COPYRIGHT_YEAR,
            platform_name=context.get('platform_name'),
            reserved=reserved)

    # Translators:  This text is bound to the HTML 'title' element of the page and appears
    # in the browser title bar when a requested certificate is not found or recognized
    context['document_title'] = _("Invalid Certificate")

    # Translators: The & characters represent an ampersand character and can be ignored
    context['company_tos_urltext'] = _("Terms of Service & Honor Code")

    # Translators: A 'Privacy Policy' is a legal document/statement describing a website's use of personal information
    context['company_privacy_urltext'] = _("Privacy Policy")

    # Translators: This line appears as a byline to a header image and describes the purpose of the page
    context['logo_subtitle'] = _("Certificate Validation")
    invalid_template_path = 'certificates/invalid.html'

    # Kick the user back to the "Invalid" screen if the feature is disabled
    if not settings.FEATURES.get('CERTIFICATES_HTML_VIEW', False):
        return render_to_response(invalid_template_path, context)

    # Load the core building blocks for the view context
    try:
        course_key = CourseKey.from_string(course_id)
        user = User.objects.get(id=user_id)
        course = modulestore().get_course(course_key)

        if not course:
            raise CourseDoesNotExist

        # Attempt to load the user's generated certificate data
        user_certificate = GeneratedCertificate.objects.get(
            user=user, course_id=course_key)

    # If there's no generated certificate data for this user, we need to see if we're in 'preview' mode...
    # If we are, we'll need to create a mock version of the user_certificate container for previewing
    except GeneratedCertificate.DoesNotExist:
        if request.GET.get('preview', None):
            user_certificate = GeneratedCertificate(
                mode=request.GET.get('preview'),
                verify_uuid=unicode(uuid4().hex),
                modified_date=datetime.now().date())
        else:
            return render_to_response(invalid_template_path, context)

    # For any other expected exceptions, kick the user back to the "Invalid" screen
    except (InvalidKeyError, CourseDoesNotExist, User.DoesNotExist):
        return render_to_response(invalid_template_path, context)

    # Badge Request Event Tracking Logic
    if 'evidence_visit' in request.GET:
        try:
            badge = BadgeAssertion.objects.get(user=user, course_id=course_key)
            tracker.emit(
                'edx.badge.assertion.evidence_visited', {
                    'user_id': user.id,
                    'course_id': unicode(course_key),
                    'enrollment_mode': badge.mode,
                    'assertion_id': badge.id,
                    'assertion_image_url': badge.data['image'],
                    'assertion_json_url': badge.data['json']['id'],
                    'issuer': badge.data['issuer'],
                })
        except BadgeAssertion.DoesNotExist:
            logger.warn(
                "Could not find badge for %s on course %s.",
                user.id,
                course_key,
            )

    # Okay, now we have all of the pieces, time to put everything together

    # 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, request.GET.get('preview'))
    if active_configuration is None:
        return render_to_response(invalid_template_path, context)
    else:
        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/Override the existing view context values with request-time values
    _update_certificate_context(context, course, user, user_certificate)

    # Microsites will need to be able to override any hard coded
    # content that was put into the context in the
    # _update_certificate_context() call above. For example the
    # 'company_about_description' talks about edX, which we most likely
    # do not want to keep in a microsite
    #
    # So we need to re-apply any configuration/content that
    # we are sourceing from the database. This is somewhat duplicative of
    # the code at the beginning of this method, but we
    # need the configuration at the top as some error code paths
    # require that to be set up early on in the pipeline
    #
    microsite_config_key = microsite.get_value('microsite_config_key')
    if microsite_config_key:
        context.update(configuration.get(microsite_config_key, {}))

    # track certificate evidence_visited event for analytics when certificate_user and accessing_user are different
    if request.user and request.user.id != user.id:
        emit_certificate_event(
            'evidence_visited', user, course_id, course, {
                'certificate_id': user_certificate.verify_uuid,
                'enrollment_mode': user_certificate.mode,
                'social_network': CertificateSocialNetworks.linkedin
            })

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

    # FINALLY, generate and send the output the client
    return render_to_response("certificates/valid.html", context)
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.id, 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}
            ),
        }
    }
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.id, 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),
        '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 #29
0
    def setUp(self):
        CourseFactory.create(org='TESTX',
                             number='1',
                             display_name='TEST1',
                             start=datetime.datetime(2010,
                                                     5,
                                                     12,
                                                     2,
                                                     42,
                                                     tzinfo=utc),
                             end=datetime.datetime(2011,
                                                   5,
                                                   12,
                                                   2,
                                                   42,
                                                   tzinfo=utc))
        CourseFactory.create(org='TESTX',
                             number='2',
                             display_name='TEST2',
                             start=datetime.datetime(2010,
                                                     5,
                                                     12,
                                                     2,
                                                     42,
                                                     tzinfo=utc),
                             end=datetime.datetime(2011,
                                                   5,
                                                   12,
                                                   2,
                                                   42,
                                                   tzinfo=utc))
        CourseFactory.create(org='TESTX',
                             number='3',
                             display_name='TEST3',
                             start=datetime.datetime(2010,
                                                     5,
                                                     12,
                                                     2,
                                                     42,
                                                     tzinfo=utc),
                             end=datetime.datetime(2011,
                                                   5,
                                                   12,
                                                   2,
                                                   42,
                                                   tzinfo=utc))

        self.fred = fred = User(username='******', email='*****@*****.**')
        fred.save()
        UserProfile(user=fred, name='Fred Flintstone').save()
        LinkedIn(user=fred, has_linkedin_account=True).save()
        self.barney = barney = User(username='******',
                                    email='*****@*****.**')
        barney.save()

        LinkedIn(user=barney, has_linkedin_account=True).save()
        UserProfile(user=barney, name='Barney Rubble').save()

        self.adam = adam = User(username='******', email='*****@*****.**')
        adam.save()

        LinkedIn(user=adam, has_linkedin_account=True).save()
        UserProfile(user=adam, name='Adam (חיים פּלי)').save()
        self.cert1 = cert1 = GeneratedCertificate(
            status='downloadable',
            user=fred,
            course_id='TESTX/1/TEST1',
            name='TestX/Intro101',
            download_url='http://test.foo/test')
        cert1.save()
        cert2 = GeneratedCertificate(status='downloadable',
                                     user=fred,
                                     course_id='TESTX/2/TEST2')
        cert2.save()
        cert3 = GeneratedCertificate(status='downloadable',
                                     user=barney,
                                     course_id='TESTX/3/TEST3')
        cert3.save()
        cert5 = GeneratedCertificate(status='downloadable',
                                     user=adam,
                                     course_id='TESTX/3/TEST3')
        cert5.save()
Exemple #30
0
class CertificateBase(object):

    type = ''  # Honor, Simple or Others
    user_id = 0
    percent = 0  # 0-100

    # HTML File
    source_dir = ''
    source_files = {}
    course_id = ''
    course_name = ''

    certificate_obj = None
    course_key = None

    certsys = None

    @classmethod
    def from_string(cls, str, certsys):
        params = str.split(',')
        return cls(params[0], params[1], params[2], certsys)

    def __init__(self, user_id, percent, type, certsys):
        self.user_id = user_id
        self.percent = percent
        self.type = type.strip().strip('"')
        self.certsys = certsys

        self.course_key = CourseKey.from_string(self.course_id)

        try:
            self.certificate_obj = GeneratedCertificate.objects.get(user_id=self.user_id, course_id=self.course_key)
        except GeneratedCertificate.DoesNotExist:
            pass

    def generate(self):

        # Get source file
        source_file = self.source_files[self.type] if self.type in self.source_files else None

        if source_file is None:
            print "Wanted to generate certificate for user_id=%s with mode=%s but have no file specified" % \
                  (self.user_id, self.type)
            return False

        source_file = "%s/%s" % (self.source_dir, source_file)

        if not os.path.isfile(source_file):
            print "Wanted to generate certificate for user_id=%s with mode=%s but have no file: %s" % \
                  (self.user_id, self.type, source_file)

        utils.ensure_dir(self.pdf_local_location)

        try:
            string = utils.get_file_contents(source_file).format(
                student_name=self.student_printed_name.encode('utf-8'),
                title='{course_name}'.format(
                    student_name=self.student_printed_name.encode('utf8'),
                  course_name=self.course_name
                )
           )
        except Exception as e:
           print "File not found: %s" % source_file
           raise e

        try:
            p = Popen(['weasyprint', '--base-url', self.source_dir, '-', self.pdf_local_filename], stdin=PIPE)
            p.communicate(input=string)
        except Exception as e:
            print "Weasyprint not found"
            raise e

        print "Generated certificate for user_id=%s" % (self.user_id,)

    def create(self):
        self.certificate_obj = GeneratedCertificate(
            user_id=self.user_id,
            course_id=self.course_id,
            grade=self.grade,
            status=self.certificate_status
        )
        if self.certificate_status == 'downloadable':
            self.certificate_obj.download_url = self.pdf_url_filename
        else:
            self.certificate_obj.download_url = ''
        self.certificate_obj.save()

    def update(self):
        self.certificate_obj.grade = self.grade
        if self.certificate_status == 'downloadable':
            self.certificate_obj.download_url = self.pdf_url_filename
        else:
            self.certificate_obj.download_url = ''
        self.certificate_obj.status = self.certificate_status
        self.certificate_obj.save()

    def process(self):

        if self.certificate_obj is None:
            print "Certificate object is created"
            self.create()

        else:

            if self.certsys.strategy == 'fail':
                raise CertificateError('Strategy is set to "fail". Certificate already exists for user_id=%s' % self.user_id)

            elif self.certsys.strategy == 'update':
                print "Certificate for user_id=%s is updated" % self.user_id
                self.update()

            elif self.certsys.strategy == 'ignore':
                pass

            else:
                raise CertificateError('Strategy is unknown. Certificate for user_id=%s exists' % self.user_id)

        if self.certsys.need_generate and self.certificate_status == 'downloadable':
            self.generate()

    @property
    def grade(self):
        return float(self.percent) / 100

    @property
    def course_sha1(self):
        return self._sha1(self.course_id)

    @property
    def user_sha1(self):
        return self._sha1(str(self.user_id))

    @property
    def pdf_relative_location(self):
        return "%s/%s" % (self.course_sha1, self.user_sha1)

    @property
    def pdf_url_location(self):
        return "%s/%s" % (self.certsys.url_prefix, self.pdf_relative_location)

    @property
    def pdf_url_filename(self):
        return "%s/%s" % (self.pdf_url_location, self.certsys.file_name)

    @property
    def pdf_local_location(self):
        return "%s/%s" % (self.certsys.storage_prefix, self.pdf_relative_location)

    @property
    def pdf_local_filename(self):
        return "%s/%s" % (self.pdf_local_location, self.certsys.file_name)

    def _sha1(self, str):
        return hashlib.sha1(str + self.certsys.secret).hexdigest()

    @property
    def certificate_status(self):
        return 'downloadable' if self.type in self.source_files else 'notpassing'

    @property
    def student_name(self):
        user = User.objects.get(id=self.user_id)
        return " ".join([user.last_name, user.first_name])

    @property
    def student_printed_name(self):
        return self.student_name