Example #1
0
def regenerate_certificate_for_user(request):
    """
    Regenerate certificates for a user.

    This is meant to be used by support staff through the UI in lms/djangoapps/support

    Arguments:
        request (HttpRequest): The request object

    Returns:
        HttpResponse

    Example Usage:

        POST /certificates/regenerate
            * username: "******"
            * course_key: "edX/DemoX/Demo_Course"

        Response: 200 OK

    """
    # Check the POST parameters, returning a 400 response if they're not valid.
    params, response = _validate_post_params(request.POST)
    if response is not None:
        return response

    user = params["user"]
    course_key = params["course_key"]

    try:
        get_course_overview(course_key)
    except CourseOverview.DoesNotExist:
        msg = _("The course {course_key} does not exist").format(
            course_key=course_key)
        return HttpResponseBadRequest(msg)

    # Check that the user is enrolled in the course
    if not CourseEnrollment.is_enrolled(user, course_key):
        msg = _("User {user_id} is not enrolled in the course {course_key}"
                ).format(user_id=user.id, course_key=course_key)
        return HttpResponseBadRequest(msg)

    # Attempt to regenerate certificates
    try:
        regenerate_user_certificates(user, course_key)
    except:  # pylint: disable=bare-except
        # We are pessimistic about the kinds of errors that might get thrown by the
        # certificates API.  This may be overkill, but we're logging everything so we can
        # track down unexpected errors.
        log.exception(
            f"Could not regenerate certificate for user {user.id} in course {course_key}"
        )
        return HttpResponseServerError(
            _("An unexpected error occurred while regenerating certificates."))

    log.info(
        f"Started regenerating certificates for user {user.id} in course {course_key} from the support page."
    )
    return HttpResponse(200)
Example #2
0
 def test_get_course_overview(self):
     """
     Test for `get_course_overview` function to retrieve a single course overview.
     """
     course_overview = CourseOverviewFactory.create()
     retrieved_course_overview = get_course_overview(course_overview.id)
     assert course_overview.id == retrieved_course_overview.id
Example #3
0
def emit_certificate_event(event_name,
                           user,
                           course_id,
                           course_overview=None,
                           event_data=None):
    """
    Emits certificate event.

    Documentation (that is not up to date) for these events can be found here:
    https://github.com/edx/edx-documentation/blob/master/en_us/data/source/internal_data_formats/tracking_logs/student_event_types.rst # pylint: disable=line-too-long
    """
    event_name = '.'.join(['edx', 'certificate', event_name])

    if not course_overview:
        course_overview = get_course_overview(course_id)

    context = {'org_id': course_overview.org, 'course_id': str(course_id)}

    data = {
        'user_id':
        user.id,
        'course_id':
        str(course_id),
        'certificate_url':
        get_certificate_url(user.id,
                            course_id,
                            uuid=event_data['certificate_id'])
    }
    event_data = event_data or {}
    event_data.update(data)

    with tracker.get_tracker().context(event_name, context):
        tracker.emit(event_name, event_data)
Example #4
0
 def _load_course_overview_with_cache(self, course_key, course_cache):
     """Retrieve the course-overview for the given course-key and store it."""
     course_overview = (course_cache[course_key]
                        if course_key in course_cache else
                        get_course_overview(course_key))
     course_cache[course_key] = course_overview
     return course_overview
Example #5
0
def generate_user_certificates(student, course_key, insecure=False, generation_mode='batch', forced_grade=None):
    """
    It will add the add-cert request into the xqueue.

    A new record will be created to track the certificate
    generation task.  If an error occurs while adding the certificate
    to the queue, the task will have status 'error'. It also emits
    `edx.certificate.created` event for analytics.

    This method has not yet been updated (it predates the certificates revamp). If modifying this method,
    see also generate_user_certificates() in generation_handler.py (which is very similar but is not called from a
    celery task). In the future these methods will be unified.

   Args:
        student (User)
        course_key (CourseKey)

    Keyword Arguments:
        insecure - (Boolean)
        generation_mode - who has requested certificate generation. Its value should `batch`
        in case of django command and `self` if student initiated the request.
        forced_grade - a string indicating to replace grade parameter. if present grading
                       will be skipped.
    """
    beta_testers_queryset = list_with_level(course_key, 'beta')
    if beta_testers_queryset.filter(username=student.username):
        log.info(f"Canceling Certificate Generation task for user {student.id} : {course_key}. User is a Beta Tester.")
        return

    xqueue = XQueueCertInterface()
    if insecure:
        xqueue.use_https = False

    course_overview = get_course_overview(course_key)
    generate_pdf = not has_html_certificates_enabled(course_overview)

    cert = xqueue.add_cert(
        student,
        course_key,
        generate_pdf=generate_pdf,
        forced_grade=forced_grade
    )

    log.info(f"Queued Certificate Generation task for {student.id} : {course_key}")

    # If cert_status is not present in certificate valid_statuses (for example unverified) then
    # add_cert returns None and raises AttributeError while accessing cert attributes.
    if cert is None:
        return

    if CertificateStatuses.is_passing_status(cert.status):
        emit_certificate_event('created', student, course_key, course_overview, {
            'user_id': student.id,
            'course_id': str(course_key),
            'certificate_id': cert.verify_uuid,
            'enrollment_mode': cert.mode,
            'generation_mode': generation_mode
        })
    return cert.status
Example #6
0
def emit_certificate_event(event_name,
                           user,
                           course_id,
                           course_overview=None,
                           event_data=None):
    """
    Utility function responsible for emitting certificate events.

    We currently track the following events:
    - `edx.certificate.created` - Emit when a course certificate with the `downloadable` status has been awarded to a
                                  learner.
    - `edx.certificate.revoked`- Emit when a course certificate with the `downloadable` status has been taken away from
                                 a learner.
    - `edx.certificate.shared` - Emit when a learner shares their course certificate to social media (LinkedIn,
                                 Facebook, or Twitter).
    - `edx.certificate.evidence_visisted` - Emit when a user (other than the learner who owns a certificate) views a
                                            course certificate (e.g., someone views a course certificate shared on a
                                            LinkedIn profile).

    Args:
        event_name (String) - Text describing the action/event that we are tracking. Examples include `revoked`,
                              `created`, etc.
        user (User) - The User object of the learner associated with this event.
        course_id (CourseLocator) - The course-run key associated with this event.
        course_overview (CourseOverview) - Optional. The CourseOverview of the course-run associated with this event.
        event_data (dictionary) - Optional. Dictionary containing any additional data we want to be associated with an
                                  event.
    """
    event_name = '.'.join(['edx', 'certificate', event_name])

    if not course_overview:
        course_overview = get_course_overview(course_id)

    context = {'org_id': course_overview.org, 'course_id': str(course_id)}

    data = {
        'user_id':
        user.id,
        'course_id':
        str(course_id),
        'certificate_url':
        get_certificate_url(user.id,
                            course_id,
                            uuid=event_data['certificate_id'])
    }
    event_data = event_data or {}
    event_data.update(data)

    with tracker.get_tracker().context(event_name, context):
        tracker.emit(event_name, event_data)
Example #7
0
    def _generate_cert(self, cert, student, grade_contents, template_pdf,
                       generate_pdf):
        """
        Generate a certificate for the student. If `generate_pdf` is True,
        sends a request to XQueue.
        """
        course_id = str(cert.course_id)
        course_overview = get_course_overview(course_id)

        key = make_hashkey(random.random())
        cert.key = key
        contents = {
            'action': 'create',
            'username': student.username,
            'course_id': course_id,
            'course_name': course_overview.display_name or course_id,
            'name': cert.name,
            'grade': grade_contents,
            'template_pdf': template_pdf,
        }
        if generate_pdf:
            cert.status = status.generating
        else:
            cert.status = status.downloadable
            cert.verify_uuid = uuid4().hex

        cert.save()
        logging.info(
            'certificate generated for user: %s with generate_pdf status: %s',
            student.username, generate_pdf)

        if generate_pdf:
            try:
                self._send_to_xqueue(contents, key)
            except XQueueAddToQueueError as exc:
                cert.status = ExampleCertificate.STATUS_ERROR
                cert.error_reason = str(exc)
                cert.save()
                LOGGER.critical(
                    ("Could not add certificate task to XQueue.  "
                     "The course was '%s' and the student was '%s'."
                     "The certificate task status has been marked as 'error' "
                     "and can be re-submitted with a management command."),
                    course_id, student.id)
            else:
                LOGGER.info(("The certificate status has been set to '%s'.  "
                             "Sent a certificate grading task to the XQueue "
                             "with the key '%s'. "), cert.status, key)
        return cert
def regenerate_user_certificates(student,
                                 course_key,
                                 course=None,
                                 forced_grade=None,
                                 template_file=None,
                                 insecure=False):
    """
    Add the regen-cert request into the xqueue.

    A new record will be created to track the certificate
    generation task.  If an error occurs while adding the certificate
    to the queue, the task will have status 'error'.

    This method has not yet been updated (it predates the certificates revamp).

    Args:
        student (User)
        course_key (CourseKey)

    Keyword Arguments:
        course (Course): Optionally provide the course object; if not provided
            it will be loaded.
        grade_value - The grade string, such as "Distinction"
        template_file - The template file used to render this certificate
        insecure - (Boolean)
    """
    if can_generate_certificate_task(student, course_key):
        log.info(
            f"{course_key} is using V2 certificates. Attempt will be made to regenerate a V2 certificate for "
            f"user {student.id}.")
        return generate_certificate_task(student, course_key)

    xqueue = XQueueCertInterface()
    if insecure:
        xqueue.use_https = False

    course_overview = get_course_overview(course_key)
    generate_pdf = not has_html_certificates_enabled(course_overview)
    log.info(
        f"Started regenerating certificates for user {student.id} in course {course_key} with generate_pdf "
        f"status: {generate_pdf}.")

    xqueue.regen_cert(student,
                      course_key,
                      course=course,
                      forced_grade=forced_grade,
                      template_file=template_file,
                      generate_pdf=generate_pdf)
    return True
def _can_generate_certificate_common(user, course_key):
    """
    Check if a course certificate can be generated (created if it doesn't already exist, or updated if it does
    exist) for this user, in this course run.

    This method contains checks that are common to both allowlist and V2 regular course certificates.
    """
    if CertificateInvalidation.has_certificate_invalidation(user, course_key):
        # The invalidation list prevents certificate generation
        log.info(
            f'{user.id} : {course_key} is on the certificate invalidation list. Certificate cannot be generated.'
        )
        return False

    enrollment_mode, __ = CourseEnrollment.enrollment_mode_for_user(
        user, course_key)
    if enrollment_mode is None:
        log.info(
            f'{user.id} : {course_key} does not have an enrollment. Certificate cannot be generated.'
        )
        return False

    if not modes_api.is_eligible_for_certificate(enrollment_mode):
        log.info(
            f'{user.id} : {course_key} has an enrollment mode of {enrollment_mode}, which is not eligible for a '
            f'certificate. Certificate cannot be generated.')
        return False

    if not IDVerificationService.user_is_verified(user):
        log.info(
            f'{user.id} does not have a verified id. Certificate cannot be generated for {course_key}.'
        )
        return False

    if not _can_generate_certificate_for_status(user, course_key):
        return False

    course_overview = get_course_overview(course_key)
    if not has_html_certificates_enabled(course_overview):
        log.info(
            f'{course_key} does not have HTML certificates enabled. Certificate cannot be generated for '
            f'{user.id}.')
        return False

    return True
Example #10
0
def _can_set_cert_status_common(user, course_key):
    """
    Determine whether we can set a custom (non-downloadable) cert status
    """
    if _is_cert_downloadable(user, course_key):
        return False

    enrollment_mode, __ = CourseEnrollment.enrollment_mode_for_user(user, course_key)
    if enrollment_mode is None:
        return False

    if not modes_api.is_eligible_for_certificate(enrollment_mode):
        return False

    course_overview = get_course_overview(course_key)
    if not has_html_certificates_enabled_from_course_overview(course_overview):
        return False

    return True
Example #11
0
def _course_from_key(course_key):
    """
    Returns the course overview
    """
    return get_course_overview(_safe_course_key(course_key))