예제 #1
0
def request_certificate(request):
    """Request the on-demand creation of a certificate for some user, course.

    A request doesn't imply a guarantee that such a creation will take place.
    We intentionally use the same machinery as is used for doing certification
    at the end of a course run, so that we can be sure users get graded and
    then if and only if they pass, do they get a certificate issued.
    """
    if request.method == "POST":
        if request.user.is_authenticated:
            username = request.user.username
            student = User.objects.get(username=username)
            course_key = CourseKey.from_string(request.POST.get('course_id'))
            course = modulestore().get_course(course_key, depth=2)

            status = certificate_status_for_student(student,
                                                    course_key)['status']
            if can_generate_certificate_task(student, course_key):
                log.info(
                    f'{course_key} is using V2 course certificates. Attempt will be made to generate a V2 '
                    f'certificate for user {student.id}.')
                generate_certificate_task(student, course_key)
            elif status in [
                    CertificateStatuses.unavailable,
                    CertificateStatuses.notpassing, CertificateStatuses.error
            ]:
                log_msg = 'Grading and certification requested for user %s in course %s via /request_certificate call'
                log.info(log_msg, username, course_key)
                status = generate_user_certificates(student,
                                                    course_key,
                                                    course=course)
            return HttpResponse(json.dumps({'add_status': status}),
                                content_type='application/json')  # pylint: disable=http-response-with-content-type-json, http-response-with-json-dumps
        return HttpResponse(json.dumps({'add_status': 'ERRORANONYMOUSUSER'}),
                            content_type='application/json')  # pylint: disable=http-response-with-content-type-json, http-response-with-json-dumps
예제 #2
0
파일: api.py 프로젝트: cmscom/edx-platform
def certificate_downloadable_status(student, course_key):
    """
    Check the student existing certificates against a given course.
    if status is not generating and not downloadable or error then user can view the generate button.

    Args:
        student (user object): logged-in user
        course_key (CourseKey): ID associated with the course

    Returns:
        Dict containing student passed status also download url, uuid for cert if available
    """
    current_status = certificate_status_for_student(student, course_key)

    # If the certificate status is an error user should view that status is "generating".
    # On the back-end, need to monitor those errors and re-submit the task.

    response_data = {
        'is_downloadable': False,
        'is_generating': True if current_status['status'] in [CertificateStatuses.generating,
                                                              CertificateStatuses.error] else False,
        'is_unverified': True if current_status['status'] == CertificateStatuses.unverified else False,
        'download_url': None,
        'uuid': None,
    }
    may_view_certificate = CourseOverview.get_from_id(course_key).may_certify()

    if current_status['status'] == CertificateStatuses.downloadable and may_view_certificate:
        response_data['is_downloadable'] = True
        response_data['download_url'] = current_status['download_url'] or get_certificate_url(student.id, course_key)
        response_data['uuid'] = current_status['uuid']

    return response_data
예제 #3
0
def request_certificate(request):
    """Request the on-demand creation of a certificate for some user, course.
    A request doesn't imply a guarantee that such a creation will take place.
    We intentionally use the same machinery as is used for doing certification
    at the end of a course run, so that we can be sure users get graded and
    then if and only if they pass, do they get a certificate issued.
    """
    if request.method == "POST":
        if request.user.is_authenticated():
            # Enter your api key here
            xqci = CertificateGeneration(
                api_key=settings.APPSEMBLER_FEATURES['ACCREDIBLE_API_KEY'])
            username = request.user.username
            student = User.objects.get(username=username)
            course_key = CourseKey.from_string(request.POST.get('course_id'))
            course = get_course(course_key)

            status = certificate_status_for_student(student,
                                                    course_key)['status']
            if status in [
                    CertificateStatuses.unavailable,
                    CertificateStatuses.notpassing, CertificateStatuses.error
            ]:
                logger.info(
                    'Grading and certification requested for user {} in course {} via /request_certificate call'
                    .format(username, course_key))
                status = xqci.add_cert(student, course_key, course=course)
            return HttpResponse(json.dumps({'add_status': status}),
                                content_type='application/json')
        return HttpResponse(json.dumps({'add_status': 'ERRORANONYMOUSUSER'}),
                            content_type='application/json')
예제 #4
0
    def test_with_downloadable_web_cert(self):
        CourseEnrollment.enroll(self.student, self.course.id, mode='honor')
        self._setup_course_certificate()
        with mock_passing_grade():
            certs_api.generate_user_certificates(self.student, self.course.id)

        cert_status = certificate_status_for_student(self.student,
                                                     self.course.id)
        self.assertEqual(
            certs_api.certificate_downloadable_status(self.student,
                                                      self.course.id),
            {
                'is_downloadable':
                True,
                'is_generating':
                False,
                'is_unverified':
                False,
                'download_url':
                '/certificates/user/{user_id}/course/{course_id}'.format(
                    user_id=self.student.id,
                    course_id=self.course.id,
                ),
                'uuid':
                cert_status['uuid']
            })
예제 #5
0
def certificate_downloadable_status(student, course_key):
    """
    Check the student existing certificates against a given course.
    if status is not generating and not downloadable or error then user can view the generate button.

    Args:
        student (user object): logged-in user
        course_key (CourseKey): ID associated with the course

    Returns:
        Dict containing student passed status also download url, uuid for cert if available
    """
    current_status = certificate_status_for_student(student, course_key)

    # If the certificate status is an error user should view that status is "generating".
    # On the back-end, need to monitor those errors and re-submit the task.

    response_data = {
        'is_downloadable': False,
        'is_generating': True if current_status['status'] in [CertificateStatuses.generating,
                                                              CertificateStatuses.error] else False,
        'is_unverified': True if current_status['status'] == CertificateStatuses.unverified else False,
        'download_url': None,
        'uuid': None,
    }
    may_view_certificate = CourseOverview.get_from_id(course_key).may_certify()

    if current_status['status'] == CertificateStatuses.downloadable and may_view_certificate:
        response_data['is_downloadable'] = True
        response_data['download_url'] = current_status['download_url'] or get_certificate_url(student.id, course_key)
        response_data['uuid'] = current_status['uuid']

    return response_data
예제 #6
0
    def test_certificate_status_for_student(self):
        student = UserFactory()
        course = CourseFactory.create(org='edx', number='verified', display_name='Verified Course')

        certificate_status = certificate_status_for_student(student, course.id)
        self.assertEqual(certificate_status['status'], CertificateStatuses.unavailable)
        self.assertEqual(certificate_status['mode'], GeneratedCertificate.MODES.honor)
예제 #7
0
 def get_has_cert(self, model):
     user = self.context['request'].user
     if user.is_authenticated():
         cert_status = certificate_status_for_student(user,
                                                      model.id)['status']
         return cert_status == 'downloadable'
     else:
         return False
    def handle(self, *args, **options):

        # Will only generate a certificate if the current
        # status is in the unavailable state, can be set
        # to something else with the force flag

        if options['force']:
            valid_statuses = getattr(CertificateStatuses, options['force'])
        else:
            valid_statuses = [CertificateStatuses.unavailable]

        if options['course']:
            # try to parse out the course from the serialized form
            try:
                course = CourseKey.from_string(options['course'])
            except InvalidKeyError:
                print(
                    "Course id {} could not be parsed as a CourseKey; falling back to SSCK.from_dep_str"
                    .format(options['course']))
                course = SlashSeparatedCourseKey.from_deprecated_string(
                    options['course'])
            ended_courses = [course]
        else:
            raise CommandError("You must specify a course")
        if options['api_key']:
            api_key = options['api_key']
        else:
            raise CommandError(
                "You must give a api_key, if don't have one visit: https://accredible.com/issuer/sign_up"
            )
        if options['styling']:
            if options['styling'] == 'True':
                new_status = "generating"
            else:
                raise CommandError(
                    "You must give true if want to do styling, no any other argument"
                )
        else:
            new_status = "downloadable"
        for course_key in ended_courses:
            # prefetch all chapters/sequentials by saying depth=2
            course = modulestore().get_course(course_key, depth=2)
            print "Fetching enrolled students for {0}".format(
                course_key.to_deprecated_string())
            enrolled_students = User.objects.filter(
                courseenrollment__course_id=course_key)
            xq = CertificateGeneration(api_key=api_key)
            total = enrolled_students.count()
            print "Total number of students: " + str(total)
            for student in enrolled_students:
                if certificate_status_for_student(
                        student, course_key)['status'] in valid_statuses:
                    ret = xq.add_cert(student,
                                      course_key,
                                      new_status,
                                      course=course)
                    print ret
예제 #9
0
    def can_view_course(cls, user, course_id):
        is_vip = cls.is_vip(user)
        cert_status = certificate_status_for_student(user, course_id)['status']

        normal_enroll = CourseEnrollment.get_enrollment(user, course_id)
        vip_enroll = VIPCourseEnrollment.objects.filter(user=user, course_id=course_id, is_active=True).exists()
        is_buyed = normal_enroll and vip_enroll

        return not (is_vip == False and cert_status != 'downloadable' and is_buyed != False)
예제 #10
0
 def test_with_downloadable_web_cert(self):
     cert_status = certificate_status_for_student(self.student,
                                                  self.course.id)
     assert certificate_downloadable_status(self.student, self.course.id) ==\
            {'is_downloadable': True,
             'is_generating': False,
             'is_unverified': False,
             'download_url': f'/certificates/{cert_status["uuid"]}',
             'is_pdf_certificate': False,
             'uuid': cert_status['uuid']}
예제 #11
0
def certificate_downloadable_status(student, course_key):
    """
    Check the student existing certificates against a given course.
    if status is not generating and not downloadable or error then user can view the generate button.

    Args:
        student (user object): logged-in user
        course_key (CourseKey): ID associated with the course

    Returns:
        Dict containing student passed status also download url, uuid for cert if available
    """
    current_status = certificate_status_for_student(student, course_key)

    # If the certificate status is an error user should view that status is "generating".
    # On the back-end, need to monitor those errors and re-submit the task.

    response_data = {
        'is_downloadable':
        False,
        'is_generating':
        True if current_status['status'] in [
            CertificateStatuses.generating,  # pylint: disable=simplifiable-if-expression
            CertificateStatuses.error
        ] else False,
        'is_unverified':
        True if current_status['status'] == CertificateStatuses.unverified else
        False,  # pylint: disable=simplifiable-if-expression
        'download_url':
        None,
        'uuid':
        None,
    }

    course_overview = CourseOverview.get_from_id(course_key)
    if (not certificates_viewable_for_course(course_overview) and
        (current_status['status'] in CertificateStatuses.PASSED_STATUSES)
            and course_overview.certificate_available_date):
        response_data['earned_but_not_available'] = True

    may_view_certificate = course_overview.may_certify()
    if current_status[
            'status'] == CertificateStatuses.downloadable and may_view_certificate:
        response_data['is_downloadable'] = True
        response_data['download_url'] = current_status[
            'download_url'] or get_certificate_url(student.id, course_key,
                                                   current_status['uuid'])
        response_data['is_pdf_certificate'] = bool(
            current_status['download_url'])
        response_data['uuid'] = current_status['uuid']

    return response_data
예제 #12
0
    def test_with_downloadable_web_cert(self):
        CourseEnrollment.enroll(self.student, self.course.id, mode='honor')
        self._setup_course_certificate()
        with mock_passing_grade():
            generate_user_certificates(self.student, self.course.id)

        cert_status = certificate_status_for_student(self.student, self.course.id)
        assert certificate_downloadable_status(self.student, self.course.id) ==\
               {'is_downloadable': True,
                'is_generating': False,
                'is_unverified': False,
                'download_url': f'/certificates/{cert_status["uuid"]}',
                'is_pdf_certificate': False,
                'uuid': cert_status['uuid']}
예제 #13
0
def issue(request, course_id):
    student = request.user

    # Extracts CourseLocator Object from course ID.
    course_key = locator.CourseLocator.from_string(course_id)
    # Extract the CourseOverview object of the course.
    course = modulestore().get_course(course_key)

    # Check the status of GeneratedCertificate in XQueue.
    certificate_status = \
        certificate_status_for_student(student, course.id)['status']

    # init with a fail, if it didn't match the conditions for issue.
    template = 'edraak_certificates/fail.html'

    # If user didn't issue or pass certificate, or an error happened
    # in the generation.
    if certificate_status in [CertificateStatuses.unavailable,
                              CertificateStatuses.notpassing,
                              CertificateStatuses.error]:
        # If the course is certifiable and the user has passed it.
        if is_certificate_allowed(student, course) and \
                is_student_pass(student, course_id):

            forced_grade = None
            if has_access(student, 'staff', course):
                forced_grade = "Pass"

            # generate the certificate
            generate_user_certificates(student, course.id,
                                       course=course, forced_grade=forced_grade)
            template = 'edraak_certificates/issue.html'

    elif certificate_status == CertificateStatuses.downloadable:
        cert = GeneratedCertificate.objects.get(
            user_id=student.id,
            course_id=course_key,
            status=CertificateStatuses.downloadable
        )
        return render_cert_by_uuid(request, cert.verify_uuid)

    return render_to_response(template, {
        'course_id': course_id,
        'user': student,
        'cert_course': course,
        'is_staff': has_access(student, 'staff', course),
        'studio_url': settings.CMS_BASE
    })
예제 #14
0
def cert_info(user, course_overview):
    """
    Get the certificate info needed to render the dashboard section for the given
    student and course.

    Arguments:
        user (User): A user.
        course_overview (CourseOverview): A course.

    Returns:
        See _cert_info
    """
    return _cert_info(
        user,
        course_overview,
        certificate_status_for_student(user, course_overview.id),
    )
예제 #15
0
    def test_with_downloadable_web_cert(self):
        CourseEnrollment.enroll(self.student, self.course.id, mode='honor')
        self._setup_course_certificate()
        with mock_passing_grade():
            certs_api.generate_user_certificates(self.student, self.course.id)

        cert_status = certificate_status_for_student(self.student, self.course.id)
        self.assertEqual(
            certs_api.certificate_downloadable_status(self.student, self.course.id),
            {
                'is_downloadable': True,
                'is_generating': False,
                'is_unverified': False,
                'download_url': '/certificates/user/{user_id}/course/{course_id}'.format(
                    user_id=self.student.id,
                    course_id=self.course.id,
                ),
                'uuid': cert_status['uuid']
            }
        )
예제 #16
0
def cert_info(user, course_overview):
    """
    Get the certificate info needed to render the dashboard section for the given
    student and course.

    Arguments:
        user (User): A user.
        course_overview (CourseOverview): A course.

    Returns:
        dict: A dictionary with keys:
            'status': one of 'generating', 'downloadable', 'notpassing', 'processing', 'restricted', 'unavailable', or
                'certificate_earned_but_not_available'
            'download_url': url, only present if show_download_url is True
            'show_survey_button': bool
            'survey_url': url, only if show_survey_button is True
            'grade': if status is not 'processing'
            'can_unenroll': if status allows for unenrollment
    """
    return _cert_info(user, course_overview,
                      certificate_status_for_student(user, course_overview.id))
예제 #17
0
def download(request, course_id):
    user = request.user
    # Extracts CourseLocator Object from course ID.
    course_key = locator.CourseLocator.from_string(course_id)
    # Extract the CourseOverview object of the course.
    course = modulestore().get_course(course_key)

    certificate_status = \
        certificate_status_for_student(user, course.id)['status']

    if certificate_status == CertificateStatuses.downloadable or is_student_pass(user, course_id):
        pdf_file = generate_certificate(request, course_id)
        file_size = os.path.getsize(pdf_file.name)
        wrapper = FileWrapper(pdf_file)
        # `application/octet-stream` is to force download
        response = HttpResponse(wrapper, content_type='application/octet-stream')
        response['Content-Length'] = file_size
        response['Content-Disposition'] = "attachment; filename=Edraak-Certificate.pdf"

        return response
    else:
        return redirect(reverse('dashboard'))
예제 #18
0
def request_certificate(request):
    """Request the on-demand creation of a certificate for some user, course.

    A request doesn't imply a guarantee that such a creation will take place.
    We intentionally use the same machinery as is used for doing certification
    at the end of a course run, so that we can be sure users get graded and
    then if and only if they pass, do they get a certificate issued.
    """
    if request.method == "POST":
        if request.user.is_authenticated():
            username = request.user.username
            student = User.objects.get(username=username)
            course_key = CourseKey.from_string(request.POST.get('course_id'))
            course = modulestore().get_course(course_key, depth=2)

            status = certificate_status_for_student(student, course_key)['status']
            if status in [CertificateStatuses.unavailable, CertificateStatuses.notpassing, CertificateStatuses.error]:
                log_msg = u'Grading and certification requested for user %s in course %s via /request_certificate call'
                log.info(log_msg, username, course_key)
                status = generate_user_certificates(student, course_key, course=course)
            return HttpResponse(json.dumps({'add_status': status}), content_type='application/json')
        return HttpResponse(json.dumps({'add_status': 'ERRORANONYMOUSUSER'}), content_type='application/json')
예제 #19
0
def cert_info(user, course_overview):
    """
    Get the certificate info needed to render the dashboard section for the given
    student and course.

    Arguments:
        user (User): A user.
        course_overview (CourseOverview): A course.

    Returns:
        dict: A dictionary with keys:
            'status': one of 'generating', 'downloadable', 'notpassing', 'processing', 'restricted', 'unavailable', or
                'certificate_earned_but_not_available'
            'download_url': url, only present if show_download_url is True
            'show_survey_button': bool
            'survey_url': url, only if show_survey_button is True
            'grade': if status is not 'processing'
            'can_unenroll': if status allows for unenrollment
    """
    return _cert_info(
        user,
        course_overview,
        certificate_status_for_student(user, course_overview.id)
    )
예제 #20
0
    def handle(self, *args, **options):
        LOGGER.info(
            (
                u"Starting to create tasks for ungenerated certificates "
                u"with arguments %s and options %s"
            ),
            text_type(args),
            text_type(options)
        )

        # Will only generate a certificate if the current
        # status is in the unavailable state, can be set
        # to something else with the force flag

        if options['force']:
            valid_statuses = [getattr(CertificateStatuses, options['force'])]
        else:
            valid_statuses = [CertificateStatuses.unavailable]

        # Print update after this many students
        status_interval = 500

        course = CourseKey.from_string(options['course'])
        ended_courses = [course]

        for course_key in ended_courses:
            # prefetch all chapters/sequentials by saying depth=2
            course = modulestore().get_course(course_key, depth=2)

            enrolled_students = User.objects.filter(
                courseenrollment__course_id=course_key
            )

            total = enrolled_students.count()
            count = 0
            start = datetime.datetime.now(UTC)

            for student in enrolled_students:
                count += 1
                if count % status_interval == 0:
                    # Print a status update with an approximation of
                    # how much time is left based on how long the last
                    # interval took
                    diff = datetime.datetime.now(UTC) - start
                    timeleft = diff * (total - count) / status_interval
                    hours, remainder = divmod(timeleft.seconds, 3600)
                    minutes, _seconds = divmod(remainder, 60)
                    print(u"{0}/{1} completed ~{2:02}:{3:02}m remaining".format(count, total, hours, minutes))
                    start = datetime.datetime.now(UTC)

                cert_status = certificate_status_for_student(student, course_key)['status']
                LOGGER.info(
                    (
                        u"Student %s has certificate status '%s' "
                        u"in course '%s'"
                    ),
                    student.id,
                    cert_status,
                    text_type(course_key)
                )

                if cert_status in valid_statuses:

                    if not options['noop']:
                        # Add the certificate request to the queue
                        ret = generate_user_certificates(
                            student,
                            course_key,
                            course=course,
                            insecure=options['insecure']
                        )

                        if ret == 'generating':
                            LOGGER.info(
                                (
                                    u"Added a certificate generation task to the XQueue "
                                    u"for student %s in course '%s'. "
                                    u"The new certificate status is '%s'."
                                ),
                                student.id,
                                text_type(course_key),
                                ret
                            )

                    else:
                        LOGGER.info(
                            (
                                u"Skipping certificate generation for "
                                u"student %s in course '%s' "
                                u"because the noop flag is set."
                            ),
                            student.id,
                            text_type(course_key)
                        )

                else:
                    LOGGER.info(
                        (
                            u"Skipped student %s because "
                            u"certificate status '%s' is not in %s"
                        ),
                        student.id,
                        cert_status,
                        text_type(valid_statuses)
                    )

            LOGGER.info(
                (
                    u"Completed ungenerated certificates command "
                    u"for course '%s'"
                ),
                text_type(course_key)
            )
예제 #21
0
    def add_cert(self,
                 student,
                 course_id,
                 forced_grade=None,
                 template_file=None,
                 generate_pdf=True):
        """
        Request a new certificate for a student.

        Arguments:
          student   - User.object
          course_id - courseenrollment.course_id (CourseKey)
          forced_grade - a string indicating a grade parameter to pass with
                         the certificate request. If this is given, grading
                         will be skipped.
          generate_pdf - Boolean should a message be sent in queue to generate certificate PDF

        Will change the certificate status to 'generating' or
        `downloadable` in case of web view certificates.

        The course must not be a CCX.

        Certificate must be in the 'unavailable', 'error',
        'deleted' or 'generating' state.

        If a student has a passing grade or is in the whitelist
        table for the course a request will be made for a new cert.

        If a student does not have a passing grade the status
        will change to status.notpassing

        Returns the newly created certificate instance
        """

        if hasattr(course_id, 'ccx'):
            LOGGER.warning(
                ("Cannot create certificate generation task for user %s "
                 "in the course '%s'; "
                 "certificates are not allowed for CCX courses."), student.id,
                str(course_id))
            return None

        valid_statuses = [
            status.generating,
            status.unavailable,
            status.deleted,
            status.error,
            status.notpassing,
            status.downloadable,
            status.auditing,
            status.audit_passing,
            status.audit_notpassing,
            status.unverified,
        ]

        cert_status_dict = certificate_status_for_student(student, course_id)
        cert_status = cert_status_dict.get('status')
        download_url = cert_status_dict.get('download_url')
        cert = None
        if download_url:
            self._log_pdf_cert_generation_discontinued_warning(
                student.id, course_id, cert_status, download_url)
            return None

        if cert_status not in valid_statuses:
            LOGGER.warning(
                ("Cannot create certificate generation task for user %s "
                 "in the course '%s'; "
                 "the certificate status '%s' is not one of %s."), student.id,
                str(course_id), cert_status, str(valid_statuses))
            return None

        profile = UserProfile.objects.get(user=student)
        profile_name = profile.name

        # Needed for access control in grading.
        self.request.user = student
        self.request.session = {}

        is_whitelisted = self.whitelist.filter(user=student,
                                               course_id=course_id,
                                               whitelist=True).exists()
        course_grade = CourseGradeFactory().read(student, course_key=course_id)
        enrollment_mode, __ = CourseEnrollment.enrollment_mode_for_user(
            student, course_id)
        mode_is_verified = enrollment_mode in GeneratedCertificate.VERIFIED_CERTS_MODES
        user_is_verified = IDVerificationService.user_is_verified(student)
        cert_mode = enrollment_mode

        is_eligible_for_certificate = modes_api.is_eligible_for_certificate(
            enrollment_mode, cert_status)
        if is_whitelisted and not is_eligible_for_certificate:
            # check if audit certificates are enabled for audit mode
            is_eligible_for_certificate = enrollment_mode != CourseMode.AUDIT or \
                not settings.FEATURES['DISABLE_AUDIT_CERTIFICATES']

        unverified = False
        # For credit mode generate verified certificate
        if cert_mode in (CourseMode.CREDIT_MODE, CourseMode.MASTERS):
            cert_mode = CourseMode.VERIFIED

        if template_file is not None:
            template_pdf = template_file
        elif mode_is_verified and user_is_verified:
            template_pdf = "certificate-template-{id.org}-{id.course}-verified.pdf".format(
                id=course_id)
        elif mode_is_verified and not user_is_verified:
            template_pdf = "certificate-template-{id.org}-{id.course}.pdf".format(
                id=course_id)
            if CourseMode.mode_for_course(course_id, CourseMode.HONOR):
                cert_mode = GeneratedCertificate.MODES.honor
            else:
                unverified = True
        else:
            # honor code and audit students
            template_pdf = "certificate-template-{id.org}-{id.course}.pdf".format(
                id=course_id)

        LOGGER.info((
            "Certificate generated for student %s in the course: %s with template: %s. "
            "given template: %s, "
            "user is verified: %s, "
            "mode is verified: %s,"
            "generate_pdf is: %s"), student.username, str(course_id),
                    template_pdf, template_file, user_is_verified,
                    mode_is_verified, generate_pdf)
        cert, __ = GeneratedCertificate.objects.get_or_create(
            user=student, course_id=course_id)

        cert.mode = cert_mode
        cert.user = student
        cert.grade = course_grade.percent
        cert.course_id = course_id
        cert.name = profile_name
        cert.download_url = ''

        # Strip HTML from grade range label
        grade_contents = forced_grade or course_grade.letter_grade
        passing = False
        try:
            grade_contents = lxml.html.fromstring(
                grade_contents).text_content()
            passing = True
        except (TypeError, XMLSyntaxError, ParserError) as exc:
            LOGGER.info(("Could not retrieve grade for student %s "
                         "in the course '%s' "
                         "because an exception occurred while parsing the "
                         "grade contents '%s' as HTML. "
                         "The exception was: '%s'"), student.id,
                        str(course_id), grade_contents, str(exc))

        # Check if the student is whitelisted
        if is_whitelisted:
            LOGGER.info("Student %s is whitelisted in '%s'", student.id,
                        str(course_id))
            passing = True

        # If this user's enrollment is not eligible to receive a
        # certificate, mark it as such for reporting and
        # analytics. Only do this if the certificate is new, or
        # already marked as ineligible -- we don't want to mark
        # existing audit certs as ineligible.
        cutoff = settings.AUDIT_CERT_CUTOFF_DATE
        if (cutoff and cert.created_date >= cutoff
            ) and not is_eligible_for_certificate:
            cert.status = status.audit_passing if passing else status.audit_notpassing
            cert.save()
            LOGGER.info(
                "Student %s with enrollment mode %s is not eligible for a certificate.",
                student.id, enrollment_mode)
            return cert
        # If they are not passing, short-circuit and don't generate cert
        elif not passing:
            cert.status = status.notpassing
            cert.save()

            LOGGER.info(
                ("Student %s does not have a grade for '%s', "
                 "so their certificate status has been set to '%s'. "
                 "No certificate generation task was sent to the XQueue."),
                student.id, str(course_id), cert.status)
            return cert

        if unverified:
            cert.status = status.unverified
            cert.save()
            LOGGER.info(
                ("User %s has a verified enrollment in course %s "
                 "but is missing ID verification. "
                 "Certificate status has been set to unverified"),
                student.id,
                str(course_id),
            )
            return cert

        # Finally, generate the certificate and send it off.
        return self._generate_cert(cert, student, grade_contents, template_pdf,
                                   generate_pdf)
예제 #22
0
    def to_representation(self, course_overview):
        course_id = unicode(course_overview.id)
        request = self.context.get('request')
        return {
            # identifiers
            'id':
            course_id,
            'name':
            course_overview.display_name,
            'number':
            course_overview.display_number_with_default,
            'org':
            course_overview.display_org_with_default,

            # dates
            'start':
            course_overview.start,
            'start_display':
            course_overview.start_display,
            'start_type':
            course_overview.start_type,
            'end':
            course_overview.end,

            # notification info
            'subscription_id':
            course_overview.clean_id(padding_char='_'),

            # access info
            'courseware_access':
            has_access(request.user, 'load_mobile', course_overview).to_json(),

            # various URLs
            # course_image is sent in both new and old formats
            # (within media to be compatible with the new Course API)
            'media': {
                'course_image': {
                    'uri': course_overview.course_image_url,
                    'name': 'Course Image',
                }
            },
            'course_image':
            course_overview.course_image_url,
            'course_about':
            get_link_for_about_page(course_overview),
            'course_sharing_utm_parameters':
            get_encoded_course_sharing_utm_params(),
            'course_updates':
            reverse(
                'course-updates-list',
                kwargs={'course_id': course_id},
                request=request,
            ),
            'course_handouts':
            reverse(
                'course-handouts-list',
                kwargs={'course_id': course_id},
                request=request,
            ),
            'discussion_url':
            reverse(
                'discussion_course',
                kwargs={'course_id': course_id},
                request=request,
            ) if course_overview.is_discussion_tab_enabled() else None,
            'video_outline':
            reverse(
                'video-summary-list',
                kwargs={'course_id': course_id},
                request=request,
            ),
            'is_vip':
            VIPInfo.is_vip(request.user),
            'is_normal_enroll':
            not VIPCourseEnrollment.objects.filter(
                user=request.user,
                course_id=course_overview.id,
                is_active=True).exists(),
            'has_cert':
            certificate_status_for_student(
                request.user, course_overview.id)['status'] == 'downloadable',
        }
예제 #23
0
    def handle(self, *args, **options):
        LOGGER.info(
            (
                u"Starting to create tasks for ungenerated certificates "
                u"with arguments %s and options %s"
            ),
            text_type(args),
            text_type(options)
        )

        # Will only generate a certificate if the current
        # status is in the unavailable state, can be set
        # to something else with the force flag

        if options['force']:
            valid_statuses = [getattr(CertificateStatuses, options['force'])]
        else:
            valid_statuses = [CertificateStatuses.unavailable]

        # Print update after this many students
        status_interval = 500

        course = CourseKey.from_string(options['course'])
        ended_courses = [course]

        for course_key in ended_courses:
            # prefetch all chapters/sequentials by saying depth=2
            course = modulestore().get_course(course_key, depth=2)

            enrolled_students = User.objects.filter(
                courseenrollment__course_id=course_key
            )

            total = enrolled_students.count()
            count = 0
            start = datetime.datetime.now(UTC)

            for student in enrolled_students:
                count += 1
                if count % status_interval == 0:
                    # Print a status update with an approximation of
                    # how much time is left based on how long the last
                    # interval took
                    diff = datetime.datetime.now(UTC) - start
                    timeleft = diff * (total - count) / status_interval
                    hours, remainder = divmod(timeleft.seconds, 3600)
                    minutes, _seconds = divmod(remainder, 60)
                    print("{0}/{1} completed ~{2:02}:{3:02}m remaining".format(count, total, hours, minutes))
                    start = datetime.datetime.now(UTC)

                cert_status = certificate_status_for_student(student, course_key)['status']
                LOGGER.info(
                    (
                        u"Student %s has certificate status '%s' "
                        u"in course '%s'"
                    ),
                    student.id,
                    cert_status,
                    text_type(course_key)
                )

                if cert_status in valid_statuses:

                    if not options['noop']:
                        # Add the certificate request to the queue
                        ret = generate_user_certificates(
                            student,
                            course_key,
                            course=course,
                            insecure=options['insecure']
                        )

                        if ret == 'generating':
                            LOGGER.info(
                                (
                                    u"Added a certificate generation task to the XQueue "
                                    u"for student %s in course '%s'. "
                                    u"The new certificate status is '%s'."
                                ),
                                student.id,
                                text_type(course_key),
                                ret
                            )

                    else:
                        LOGGER.info(
                            (
                                u"Skipping certificate generation for "
                                u"student %s in course '%s' "
                                u"because the noop flag is set."
                            ),
                            student.id,
                            text_type(course_key)
                        )

                else:
                    LOGGER.info(
                        (
                            u"Skipped student %s because "
                            u"certificate status '%s' is not in %s"
                        ),
                        student.id,
                        cert_status,
                        text_type(valid_statuses)
                    )

            LOGGER.info(
                (
                    u"Completed ungenerated certificates command "
                    u"for course '%s'"
                ),
                text_type(course_key)
            )
예제 #24
0
    def add_cert(
            self,
            student,
            course_id,
            defined_status="downloadable",
            course=None,
            forced_grade=None,
            template_file=None,
            title='None'):
        """
        Request a new certificate for a student.

        Arguments:
          student   - User.object
          course_id - courseenrollment.course_id (CourseKey)
          forced_grade - a string indicating a grade parameter to pass with
                         the certificate request. If this is given, grading
                         will be skipped.

        Will change the certificate status to 'generating' or 'downloadable'.

        Certificate must be in the 'unavailable', 'error',
        'deleted' or 'generating' state.

        If a student has a passing grade or is in the whitelist
        table for the course a request will be made for a new cert.

        If a student has allow_certificate set to False in the
        userprofile table the status will change to 'restricted'

        If a student does not have a passing grade the status
        will change to status.notpassing

        Returns the student's status
        """

        VALID_STATUSES = [
            status.generating,
            status.unavailable,
            status.deleted,
            status.error,
            status.notpassing
        ]

        cert_status = certificate_status_for_student(
            student,
            course_id)['status']

        new_status = cert_status

        if cert_status in VALID_STATUSES:
            '''
            rade the student
            re-use the course passed in optionally
            so we don't have to re-fetch everything
            for every student
            '''
            if course is None:
                course = courses.get_course_by_id(course_id)

            profile = UserProfile.objects.get(user=student)
            profile_name = profile.name
            # Needed
            self.request.user = student
            self.request.session = {}
            course_name = course.display_name or course_id.to_deprecated_string()
            description = ''
            for section_key in ['short_description', 'description', 'overview']:
                loc = loc = course.location.replace(
                    category='about',
                    name=section_key
                )
                try:
                    if modulestore().get_item(loc).data:
                        description = modulestore().get_item(loc).data
                        break
                except:
                    print("this course don't have " + str(section_key))

            if not description:
                description = "course_description"
            is_whitelisted = self.whitelist.filter(
                user=student,
                course_id=course_id,
                whitelist=True).exists()

            grade = CourseGradeFactory().read(student, course)
            enrollment_mode, __ = CourseEnrollment.enrollment_mode_for_user(
                student, course_id
            )
            mode_is_verified = (
                enrollment_mode == GeneratedCertificate.MODES.verified
            )
            cert_mode = GeneratedCertificate.MODES.honor
            if forced_grade:
                grade = forced_grade

            cert, __ = GeneratedCertificate.objects.get_or_create(
                user=student,
                course_id=course_id
            )

            cert.mode = cert_mode
            cert.user = student
            cert.grade = grade.percent
            cert.course_id = course_id
            cert.name = profile_name

            # Strip HTML from grade range label
            # convert percent to points as an integer
            grade_contents = int(grade.percent * 100)

            if is_whitelisted or grade_contents is not None:

                # check to see whether the student is on the
                # the embargoed country restricted list
                # otherwise, put a new certificate request
                # on the queue
                print grade_contents
                if self.restricted.filter(user=student).exists():
                    new_status = status.restricted
                    cert.status = new_status
                    cert.save()
                else:
                    contents = {
                        'action': 'create',
                        'username': student.username,
                        'course_id': course_id.to_deprecated_string(),
                        'course_name': course_name,
                        'name': profile_name,
                        'grade': grade_contents
                    }

                    if defined_status == "generating":
                        approve = False
                    else:
                        approve = True

                    # check to see if this is a BETA course
                    course_name = course_name.strip()
                    if course_name.startswith("BETA") or course_name.startswith("Beta") or course_name.startswith("beta"):
                        course_name = course_name[4:].strip()
                    grade_into_string = grade.letter_grade
                    payload = {
                        "credential":
                        {
                            "name": course_name,
                            "group_name": course_name,
                            "description": description,
                            "achievement_id": contents['course_id'],
                            "course_link": "/courses/" + contents['course_id'] + "/about",
                            "approve": approve,
                            "template_name": contents['course_id'],
                            "grade": contents['grade'],
                            "recipient": {
                                "name": contents['name'],
                                "email": student.email
                            }
                        }
                    }

                    payload = json.dumps(payload)

                    r = requests.post('https://api.accredible.com/v1/credentials', payload, headers={
                                      'Authorization': 'Token token=' + self.api_key, 'Content-Type': 'application/json'})
                    if r.status_code == 200:
                        json_response = r.json()
                        cert.status = defined_status
                        cert.key = json_response["credential"]["id"]
                        if 'private' in json_response:
                            cert.download_url = "https://wwww.accredible.com/" + \
                                str(json_response["credential"]["id"]) + \
                                "?key" + str(json_response["private_key"])
                        else:
                            cert.download_url = "https://www.accredible.com/" + \
                                str(cert.key)
                        cert.save()
                    else:
                        new_status = "errors"
            else:
                cert_status = status.notpassing
                cert.status = cert_status
                cert.save()

        return new_status
예제 #25
0
    def handle(self, *args, **options):
        LOGGER.info(("Starting to create tasks for ungenerated certificates "
                     "with arguments %s and options %s"), str(args),
                    str(options))

        # Will only generate a certificate if the current
        # status is in the unavailable state, can be set
        # to something else with the force flag

        if options['force']:
            valid_statuses = [getattr(CertificateStatuses, options['force'])]
        else:
            valid_statuses = [CertificateStatuses.unavailable]

        # Print update after this many students
        status_interval = 500

        course = CourseKey.from_string(options['course'])
        ended_courses = [course]

        for course_key in ended_courses:
            enrolled_students = User.objects.filter(
                courseenrollment__course_id=course_key)

            total = enrolled_students.count()
            count = 0
            start = datetime.datetime.now(UTC)

            for student in enrolled_students:
                count += 1
                if count % status_interval == 0:
                    # Print a status update with an approximation of
                    # how much time is left based on how long the last
                    # interval took
                    diff = datetime.datetime.now(UTC) - start
                    timeleft = diff * (total - count) / status_interval
                    hours, remainder = divmod(timeleft.seconds, 3600)
                    minutes, _seconds = divmod(remainder, 60)
                    print(
                        f"{count}/{total} completed ~{hours:02}:{minutes:02}m remaining"
                    )
                    start = datetime.datetime.now(UTC)

                cert_status = certificate_status_for_student(
                    student, course_key)['status']
                LOGGER.info(("Student %s has certificate status '%s' "
                             "in course '%s'"), student.id, cert_status,
                            str(course_key))

                if cert_status in valid_statuses:

                    if not options['noop']:
                        # Add the certificate request to the queue
                        generate_user_certificates(
                            student, course_key, insecure=options['insecure'])

                        LOGGER.info(
                            f"Added a certificate generation task to the XQueue for student {student.id} in "
                            f"course {course_key}.")

                    else:
                        LOGGER.info(("Skipping certificate generation for "
                                     "student %s in course '%s' "
                                     "because the noop flag is set."),
                                    student.id, str(course_key))

                else:
                    LOGGER.info(("Skipped student %s because "
                                 "certificate status '%s' is not in %s"),
                                student.id, cert_status, str(valid_statuses))

            LOGGER.info(("Completed ungenerated certificates command "
                         "for course '%s'"), str(course_key))
예제 #26
0
    def add_cert(self, student, course_id, course=None, forced_grade=None, template_file=None, generate_pdf=True):
        """
        Request a new certificate for a student.

        Arguments:
          student   - User.object
          course_id - courseenrollment.course_id (CourseKey)
          forced_grade - a string indicating a grade parameter to pass with
                         the certificate request. If this is given, grading
                         will be skipped.
          generate_pdf - Boolean should a message be sent in queue to generate certificate PDF

        Will change the certificate status to 'generating' or
        `downloadable` in case of web view certificates.

        The course must not be a CCX.

        Certificate must be in the 'unavailable', 'error',
        'deleted' or 'generating' state.

        If a student has a passing grade or is in the whitelist
        table for the course a request will be made for a new cert.

        If a student has allow_certificate set to False in the
        userprofile table the status will change to 'restricted'

        If a student does not have a passing grade the status
        will change to status.notpassing

        Returns the newly created certificate instance
        """

        if hasattr(course_id, 'ccx'):
            LOGGER.warning(
                (
                    u"Cannot create certificate generation task for user %s "
                    u"in the course '%s'; "
                    u"certificates are not allowed for CCX courses."
                ),
                student.id,
                unicode(course_id)
            )
            return None

        valid_statuses = [
            status.generating,
            status.unavailable,
            status.deleted,
            status.error,
            status.notpassing,
            status.downloadable,
            status.auditing,
            status.audit_passing,
            status.audit_notpassing,
            status.unverified,
        ]

        cert_status_dict = certificate_status_for_student(student, course_id)
        cert_status = cert_status_dict.get('status')
        download_url = cert_status_dict.get('download_url')
        cert = None
        if download_url:
            self._log_pdf_cert_generation_discontinued_warning(
                student.id, course_id, cert_status, download_url
            )
            return None

        if cert_status not in valid_statuses:
            LOGGER.warning(
                (
                    u"Cannot create certificate generation task for user %s "
                    u"in the course '%s'; "
                    u"the certificate status '%s' is not one of %s."
                ),
                student.id,
                unicode(course_id),
                cert_status,
                unicode(valid_statuses)
            )
            return None

        # The caller can optionally pass a course in to avoid
        # re-fetching it from Mongo. If they have not provided one,
        # get it from the modulestore.
        if course is None:
            course = modulestore().get_course(course_id, depth=0)

        profile = UserProfile.objects.get(user=student)
        profile_name = profile.name

        # Needed for access control in grading.
        self.request.user = student
        self.request.session = {}

        is_whitelisted = self.whitelist.filter(user=student, course_id=course_id, whitelist=True).exists()
        course_grade = CourseGradeFactory().read(student, course)
        enrollment_mode, __ = CourseEnrollment.enrollment_mode_for_user(student, course_id)
        mode_is_verified = enrollment_mode in GeneratedCertificate.VERIFIED_CERTS_MODES
        user_is_verified = IDVerificationService.user_is_verified(student)
        cert_mode = enrollment_mode
        is_eligible_for_certificate = is_whitelisted or CourseMode.is_eligible_for_certificate(enrollment_mode)
        unverified = False
        # For credit mode generate verified certificate
        if cert_mode in (CourseMode.CREDIT_MODE, CourseMode.MASTERS):
            cert_mode = CourseMode.VERIFIED

        if template_file is not None:
            template_pdf = template_file
        elif mode_is_verified and user_is_verified:
            template_pdf = "certificate-template-{id.org}-{id.course}-verified.pdf".format(id=course_id)
        elif mode_is_verified and not user_is_verified:
            template_pdf = "certificate-template-{id.org}-{id.course}.pdf".format(id=course_id)
            if CourseMode.mode_for_course(course_id, CourseMode.HONOR):
                cert_mode = GeneratedCertificate.MODES.honor
            else:
                unverified = True
        else:
            # honor code and audit students
            template_pdf = "certificate-template-{id.org}-{id.course}.pdf".format(id=course_id)

        LOGGER.info(
            (
                u"Certificate generated for student %s in the course: %s with template: %s. "
                u"given template: %s, "
                u"user is verified: %s, "
                u"mode is verified: %s,"
                u"generate_pdf is: %s"
            ),
            student.username,
            unicode(course_id),
            template_pdf,
            template_file,
            user_is_verified,
            mode_is_verified,
            generate_pdf
        )

        cert, created = GeneratedCertificate.objects.get_or_create(user=student, course_id=course_id)

        cert.mode = cert_mode
        cert.user = student
        cert.grade = course_grade.percent
        cert.course_id = course_id
        cert.name = profile_name
        cert.download_url = ''

        # Strip HTML from grade range label
        grade_contents = forced_grade or course_grade.letter_grade
        try:
            grade_contents = lxml.html.fromstring(grade_contents).text_content()
            passing = True
        except (TypeError, XMLSyntaxError, ParserError) as exc:
            LOGGER.info(
                (
                    u"Could not retrieve grade for student %s "
                    u"in the course '%s' "
                    u"because an exception occurred while parsing the "
                    u"grade contents '%s' as HTML. "
                    u"The exception was: '%s'"
                ),
                student.id,
                unicode(course_id),
                grade_contents,
                unicode(exc)
            )

            # Log if the student is whitelisted
            if is_whitelisted:
                LOGGER.info(
                    u"Student %s is whitelisted in '%s'",
                    student.id,
                    unicode(course_id)
                )
                passing = True
            else:
                passing = False

        # If this user's enrollment is not eligible to receive a
        # certificate, mark it as such for reporting and
        # analytics. Only do this if the certificate is new, or
        # already marked as ineligible -- we don't want to mark
        # existing audit certs as ineligible.
        cutoff = settings.AUDIT_CERT_CUTOFF_DATE
        if (cutoff and cert.created_date >= cutoff) and not is_eligible_for_certificate:
            cert.status = status.audit_passing if passing else status.audit_notpassing
            cert.save()
            LOGGER.info(
                u"Student %s with enrollment mode %s is not eligible for a certificate.",
                student.id,
                enrollment_mode
            )
            return cert
        # If they are not passing, short-circuit and don't generate cert
        elif not passing:
            cert.status = status.notpassing
            cert.save()

            LOGGER.info(
                (
                    u"Student %s does not have a grade for '%s', "
                    u"so their certificate status has been set to '%s'. "
                    u"No certificate generation task was sent to the XQueue."
                ),
                student.id,
                unicode(course_id),
                cert.status
            )
            return cert

        # Check to see whether the student is on the the embargoed
        # country restricted list. If so, they should not receive a
        # certificate -- set their status to restricted and log it.
        if self.restricted.filter(user=student).exists():
            cert.status = status.restricted
            cert.save()

            LOGGER.info(
                (
                    u"Student %s is in the embargoed country restricted "
                    u"list, so their certificate status has been set to '%s' "
                    u"for the course '%s'. "
                    u"No certificate generation task was sent to the XQueue."
                ),
                student.id,
                cert.status,
                unicode(course_id)
            )
            return cert

        if unverified:
            cert.status = status.unverified
            cert.save()
            LOGGER.info(
                (
                    u"User %s has a verified enrollment in course %s "
                    u"but is missing ID verification. "
                    u"Certificate status has been set to unverified"
                ),
                student.id,
                unicode(course_id),
            )
            return cert

        # Finally, generate the certificate and send it off.
        return self._generate_cert(cert, course, student, grade_contents, template_pdf, generate_pdf)
예제 #27
0
    def add_cert(self, student, course_id, course=None, forced_grade=None, template_file=None, generate_pdf=True):
        """
        Request a new certificate for a student.

        Arguments:
          student   - User.object
          course_id - courseenrollment.course_id (CourseKey)
          forced_grade - a string indicating a grade parameter to pass with
                         the certificate request. If this is given, grading
                         will be skipped.
          generate_pdf - Boolean should a message be sent in queue to generate certificate PDF

        Will change the certificate status to 'generating' or
        `downloadable` in case of web view certificates.

        The course must not be a CCX.

        Certificate must be in the 'unavailable', 'error',
        'deleted' or 'generating' state.

        If a student has a passing grade or is in the whitelist
        table for the course a request will be made for a new cert.

        If a student has allow_certificate set to False in the
        userprofile table the status will change to 'restricted'

        If a student does not have a passing grade the status
        will change to status.notpassing

        Returns the newly created certificate instance
        """

        if hasattr(course_id, 'ccx'):
            LOGGER.warning(
                (
                    u"Cannot create certificate generation task for user %s "
                    u"in the course '%s'; "
                    u"certificates are not allowed for CCX courses."
                ),
                student.id,
                unicode(course_id)
            )
            return None

        valid_statuses = [
            status.generating,
            status.unavailable,
            status.deleted,
            status.error,
            status.notpassing,
            status.downloadable,
            status.auditing,
            status.audit_passing,
            status.audit_notpassing,
            status.unverified,
        ]

        cert_status = certificate_status_for_student(student, course_id)['status']
        cert = None

        if cert_status not in valid_statuses:
            LOGGER.warning(
                (
                    u"Cannot create certificate generation task for user %s "
                    u"in the course '%s'; "
                    u"the certificate status '%s' is not one of %s."
                ),
                student.id,
                unicode(course_id),
                cert_status,
                unicode(valid_statuses)
            )
            return None

        # The caller can optionally pass a course in to avoid
        # re-fetching it from Mongo. If they have not provided one,
        # get it from the modulestore.
        if course is None:
            course = modulestore().get_course(course_id, depth=0)

        profile = UserProfile.objects.get(user=student)
        profile_name = profile.name

        # Needed for access control in grading.
        self.request.user = student
        self.request.session = {}

        is_whitelisted = self.whitelist.filter(user=student, course_id=course_id, whitelist=True).exists()
        course_grade = CourseGradeFactory().read(student, course)
        enrollment_mode, __ = CourseEnrollment.enrollment_mode_for_user(student, course_id)
        mode_is_verified = enrollment_mode in GeneratedCertificate.VERIFIED_CERTS_MODES
        user_is_verified = SoftwareSecurePhotoVerification.user_is_verified(student)
        cert_mode = enrollment_mode
        is_eligible_for_certificate = is_whitelisted or CourseMode.is_eligible_for_certificate(enrollment_mode)
        unverified = False
        # For credit mode generate verified certificate
        if cert_mode == CourseMode.CREDIT_MODE:
            cert_mode = CourseMode.VERIFIED

        if template_file is not None:
            template_pdf = template_file
        elif mode_is_verified and user_is_verified:
            template_pdf = "certificate-template-{id.org}-{id.course}-verified.pdf".format(id=course_id)
        elif mode_is_verified and not user_is_verified:
            template_pdf = "certificate-template-{id.org}-{id.course}.pdf".format(id=course_id)
            if CourseMode.mode_for_course(course_id, CourseMode.HONOR):
                cert_mode = GeneratedCertificate.MODES.honor
            else:
                unverified = True
        else:
            # honor code and audit students
            template_pdf = "certificate-template-{id.org}-{id.course}.pdf".format(id=course_id)

        LOGGER.info(
            (
                u"Certificate generated for student %s in the course: %s with template: %s. "
                u"given template: %s, "
                u"user is verified: %s, "
                u"mode is verified: %s,"
                u"generate_pdf is: %s"
            ),
            student.username,
            unicode(course_id),
            template_pdf,
            template_file,
            user_is_verified,
            mode_is_verified,
            generate_pdf
        )

        cert, created = GeneratedCertificate.objects.get_or_create(user=student, course_id=course_id)

        cert.mode = cert_mode
        cert.user = student
        cert.grade = course_grade.percent
        cert.course_id = course_id
        cert.name = profile_name
        cert.download_url = ''

        # Strip HTML from grade range label
        grade_contents = forced_grade or course_grade.letter_grade
        try:
            grade_contents = lxml.html.fromstring(grade_contents).text_content()
            passing = True
        except (TypeError, XMLSyntaxError, ParserError) as exc:
            LOGGER.info(
                (
                    u"Could not retrieve grade for student %s "
                    u"in the course '%s' "
                    u"because an exception occurred while parsing the "
                    u"grade contents '%s' as HTML. "
                    u"The exception was: '%s'"
                ),
                student.id,
                unicode(course_id),
                grade_contents,
                unicode(exc)
            )

            # Log if the student is whitelisted
            if is_whitelisted:
                LOGGER.info(
                    u"Student %s is whitelisted in '%s'",
                    student.id,
                    unicode(course_id)
                )
                passing = True
            else:
                passing = False

        # If this user's enrollment is not eligible to receive a
        # certificate, mark it as such for reporting and
        # analytics. Only do this if the certificate is new, or
        # already marked as ineligible -- we don't want to mark
        # existing audit certs as ineligible.
        cutoff = settings.AUDIT_CERT_CUTOFF_DATE
        if (cutoff and cert.created_date >= cutoff) and not is_eligible_for_certificate:
            cert.status = status.audit_passing if passing else status.audit_notpassing
            cert.save()
            LOGGER.info(
                u"Student %s with enrollment mode %s is not eligible for a certificate.",
                student.id,
                enrollment_mode
            )
            return cert
        # If they are not passing, short-circuit and don't generate cert
        elif not passing:
            cert.status = status.notpassing
            cert.save()

            LOGGER.info(
                (
                    u"Student %s does not have a grade for '%s', "
                    u"so their certificate status has been set to '%s'. "
                    u"No certificate generation task was sent to the XQueue."
                ),
                student.id,
                unicode(course_id),
                cert.status
            )
            return cert

        # Check to see whether the student is on the the embargoed
        # country restricted list. If so, they should not receive a
        # certificate -- set their status to restricted and log it.
        if self.restricted.filter(user=student).exists():
            cert.status = status.restricted
            cert.save()

            LOGGER.info(
                (
                    u"Student %s is in the embargoed country restricted "
                    u"list, so their certificate status has been set to '%s' "
                    u"for the course '%s'. "
                    u"No certificate generation task was sent to the XQueue."
                ),
                student.id,
                cert.status,
                unicode(course_id)
            )
            return cert

        if unverified:
            cert.status = status.unverified
            cert.save()
            LOGGER.info(
                (
                    u"User %s has a verified enrollment in course %s "
                    u"but is missing ID verification. "
                    u"Certificate status has been set to unverified"
                ),
                student.id,
                unicode(course_id),
            )
            return cert

        # Finally, generate the certificate and send it off.
        return self._generate_cert(cert, course, student, grade_contents, template_pdf, generate_pdf)