Example #1
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']
            })
    def handle(self, *args, **options):
        """Resubmit certificates with status 'error'.

        Arguments:
            username (unicode): Identifier for the certificate's user.

        Keyword Arguments:
            course_key_list (list): List of course key strings.

        Raises:
            CommandError

        """
        only_course_keys = []
        for course_key_str in options['course_key_list']:
            try:
                only_course_keys.append(CourseKey.from_string(course_key_str))
            except InvalidKeyError:
                raise CommandError(
                    u'"{course_key_str}" is not a valid course key.'.format(
                        course_key_str=course_key_str))

        if only_course_keys:
            LOGGER.info(
                (u'Starting to re-submit certificates with status "error" '
                 u'in these courses: %s'),
                ", ".join([text_type(key) for key in only_course_keys]))
        else:
            LOGGER.info(
                u'Starting to re-submit certificates with status "error".')

        # Retrieve the IDs of generated certificates with
        # error status in the set of courses we're considering.
        queryset = (
            GeneratedCertificate.objects.select_related('user')  # pylint: disable=no-member
        ).filter(status=CertificateStatuses.error)
        if only_course_keys:
            queryset = queryset.filter(course_id__in=only_course_keys)

        resubmit_list = [(cert.user, cert.course_id) for cert in queryset]
        course_cache = {}
        resubmit_count = 0
        for user, course_key in resubmit_list:
            course = self._load_course_with_cache(course_key, course_cache)

            if course is not None:
                certs_api.generate_user_certificates(user,
                                                     course_key,
                                                     course=course)
                resubmit_count += 1
                LOGGER.info((u"Re-submitted certificate for user %s "
                             u"in course '%s'"), user.id, course_key)
            else:
                LOGGER.error(
                    (u"Could not find course for course key '%s'.  "
                     u"Certificate for user %s will not be resubmitted."),
                    course_key, user.id)

        LOGGER.info(u"Finished resubmitting %s certificate tasks",
                    resubmit_count)
Example #3
0
def generate_certificate(self, **kwargs):
    """
    Generates a certificate for a single user.

    kwargs:
        - student: The student for whom to generate a certificate.
        - course_key: The course key for the course that the student is
            receiving a certificate in.
        - expected_verification_status: The expected verification status
            for the user.  When the status has changed, we double check
            that the actual verification status is as expected before
            generating a certificate, in the off chance that the database
            has not yet updated with the user's new verification status.
    """
    original_kwargs = kwargs.copy()
    student = User.objects.get(id=kwargs.pop('student'))
    course_key = CourseKey.from_string(kwargs.pop('course_key'))
    expected_verification_status = kwargs.pop('expected_verification_status',
                                              None)
    if expected_verification_status:
        actual_verification_status = IDVerificationService.user_status(student)
        actual_verification_status = actual_verification_status['status']
        if expected_verification_status != actual_verification_status:
            logger.warning(u'Expected verification status {expected} '
                           u'differs from actual verification status {actual} '
                           u'for user {user} in course {course}'.format(
                               expected=expected_verification_status,
                               actual=actual_verification_status,
                               user=student.id,
                               course=course_key))
            raise self.retry(kwargs=original_kwargs)
    generate_user_certificates(student=student,
                               course_key=course_key,
                               **kwargs)
Example #4
0
    def test_web_certificate(self):
        CourseMode.objects.create(
            course_id=self.course.id,
            mode_display_name="Honor",
            mode_slug=CourseMode.HONOR,
        )
        self.login_and_enroll()
        certificates = [{
            'id': 1,
            'name': 'Test Certificate Name',
            'description': 'Test Certificate Description',
            'course_title': 'tes_course_title',
            'signatories': [],
            'version': 1,
            'is_active': True
        }]
        self.course.certificates = {'certificates': certificates}
        self.course.cert_html_view_enabled = True
        self.store.update_item(self.course, self.user.id)

        with mock_passing_grade():
            generate_user_certificates(self.user, self.course.id)

        response = self.api_response()
        certificate_data = response.data[0]['certificate']
        self.assertRegexpMatches(
            certificate_data['url'],
            r'http.*/certificates/user/{user_id}/course/{course_id}'.format(
                user_id=self.user.id,
                course_id=self.course.id,
            ))
Example #5
0
    def test_xqueue_submit_task_error(self):
        with mock_passing_grade():
            with self._mock_queue(is_successful=False):
                certs_api.generate_user_certificates(self.student, self.course.id)

        # Verify that the certificate has been marked with status error
        cert = GeneratedCertificate.eligible_certificates.get(user=self.student, course_id=self.course.id)
        self.assertEqual(cert.status, 'error')
        self.assertIn(self.ERROR_REASON, cert.error_reason)
Example #6
0
    def test_xqueue_submit_task_error(self):
        with mock_passing_grade():
            with self._mock_queue(is_successful=False):
                certs_api.generate_user_certificates(self.student, self.course.id)

        # Verify that the certificate has been marked with status error
        cert = GeneratedCertificate.eligible_certificates.get(user=self.student, course_id=self.course.id)
        self.assertEqual(cert.status, 'error')
        self.assertIn(self.ERROR_REASON, cert.error_reason)
Example #7
0
    def test_new_cert_requests_returns_generating_for_html_certificate(self):
        """
        Test no message sent to Xqueue if HTML certificate view is enabled
        """
        self._setup_course_certificate()
        with mock_passing_grade():
            certs_api.generate_user_certificates(self.student, self.course.id)

        # Verify that the certificate has status 'downloadable'
        cert = GeneratedCertificate.eligible_certificates.get(user=self.student, course_id=self.course.id)
        self.assertEqual(cert.status, CertificateStatuses.downloadable)
Example #8
0
    def test_new_cert_request_for_html_certificate(self):
        """
        Test generate_user_certificates with HTML certificates
        """
        self._setup_course_certificate()
        with mock_passing_grade():
            generate_user_certificates(self.student, self.course.id)

        cert = GeneratedCertificate.eligible_certificates.get(
            user=self.student, course_id=self.course.id)
        assert cert.status == CertificateStatuses.downloadable
Example #9
0
    def test_new_cert_requests_returns_generating_for_html_certificate(self):
        """
        Test no message sent to Xqueue if HTML certificate view is enabled
        """
        self._setup_course_certificate()
        with mock_passing_grade():
            certs_api.generate_user_certificates(self.student, self.course.id)

        # Verify that the certificate has status 'downloadable'
        cert = GeneratedCertificate.eligible_certificates.get(user=self.student, course_id=self.course.id)
        self.assertEqual(cert.status, CertificateStatuses.downloadable)
    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']}
Example #11
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
Example #12
0
    def test_new_cert_requests_into_xqueue_returns_generating(self):
        with mock_passing_grade():
            with self._mock_queue():
                certs_api.generate_user_certificates(self.student, self.course.id)

        # Verify that the certificate has status 'generating'
        cert = GeneratedCertificate.eligible_certificates.get(user=self.student, course_id=self.course.id)
        self.assertEqual(cert.status, CertificateStatuses.generating)
        self.assert_event_emitted(
            'edx.certificate.created',
            user_id=self.student.id,
            course_id=unicode(self.course.id),
            certificate_url=certs_api.get_certificate_url(self.student.id, self.course.id),
            certificate_id=cert.verify_uuid,
            enrollment_mode=cert.mode,
            generation_mode='batch'
        )
Example #13
0
    def test_web_certificate(self):
        CourseMode.objects.create(
            course_id=self.course.id,
            mode_display_name="Honor",
            mode_slug=CourseMode.HONOR,
        )
        self.login_and_enroll()
        self.course.cert_html_view_enabled = True
        self.store.update_item(self.course, self.user.id)

        with mock_passing_grade():
            generate_user_certificates(self.user, self.course.id)

        response = self.api_response()
        certificate_data = response.data[0]['certificate']
        self.assertRegex(certificate_data['url'],
                         r'http.*/certificates/[0-9a-f]{32}')
Example #14
0
    def test_new_cert_requests_into_xqueue_returns_generating(self):
        with mock_passing_grade():
            with self._mock_queue():
                certs_api.generate_user_certificates(self.student, self.course.id)

        # Verify that the certificate has status 'generating'
        cert = GeneratedCertificate.eligible_certificates.get(user=self.student, course_id=self.course.id)
        self.assertEqual(cert.status, CertificateStatuses.generating)
        self.assert_event_emitted(
            'edx.certificate.created',
            user_id=self.student.id,
            course_id=six.text_type(self.course.id),
            certificate_url=certs_api.get_certificate_url(self.student.id, self.course.id),
            certificate_id=cert.verify_uuid,
            enrollment_mode=cert.mode,
            generation_mode='batch'
        )
Example #15
0
    def test_cert_api_return(self, self_paced, cert_avail_delta, cert_downloadable_status, earned_but_not_available):
        """
        Test 'downloadable status'
        """
        cert_avail_date = datetime.now(pytz.UTC) + cert_avail_delta
        self.course.self_paced = self_paced
        self.course.certificate_available_date = cert_avail_date
        self.course.save()

        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)

        downloadable_status = certs_api.certificate_downloadable_status(self.student, self.course.id)
        self.assertEqual(downloadable_status['is_downloadable'], cert_downloadable_status)
        self.assertEqual(downloadable_status.get('earned_but_not_available'), earned_but_not_available)
Example #16
0
    def test_cert_api_return(self, self_paced, cert_avail_delta, cert_downloadable_status):
        """
        Test 'downloadable status'
        """
        cert_avail_date = datetime.now(pytz.UTC) + cert_avail_delta
        self.course.self_paced = self_paced
        self.course.certificate_available_date = cert_avail_date
        self.course.save()

        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)

        self.assertEqual(
            certs_api.certificate_downloadable_status(self.student, self.course.id)['is_downloadable'],
            cert_downloadable_status
        )
Example #17
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']
            }
        )
Example #18
0
    def test_web_certificate(self):
        CourseMode.objects.create(
            course_id=self.course.id,
            mode_display_name="Honor",
            mode_slug=CourseMode.HONOR,
        )
        self.login_and_enroll()
        self.course.cert_html_view_enabled = True
        self.store.update_item(self.course, self.user.id)

        with mock_passing_grade():
            generate_user_certificates(self.user, self.course.id)

        response = self.api_response()
        certificate_data = response.data[0]['certificate']
        self.assertRegexpMatches(
            certificate_data['url'],
            r'http.*/certificates/user/{user_id}/course/{course_id}'.format(
                user_id=self.user.id,
                course_id=self.course.id,
            )
        )
Example #19
0
    def test_generate_user_certificates_with_unverified_cert_status(self):
        """
        Generate user certificate when the certificate is unverified
        will trigger an update to the certificate if the user has since
        verified.
        """
        # generate certificate with unverified status.
        GeneratedCertificateFactory.create(
            user=self.student,
            course_id=self.course.id,
            status=CertificateStatuses.unverified,
            mode='verified'
        )

        with mock_passing_grade():
            with self._mock_queue():
                status = certs_api.generate_user_certificates(self.student, self.course.id)
                self.assertEqual(status, 'generating')
Example #20
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')
Example #21
0
def generate_students_certificates(
        _xmodule_instance_args, _entry_id, course_id, task_input, action_name):
    """
    For a given `course_id`, generate certificates for only students present in 'students' key in task_input
    json column, otherwise generate certificates for all enrolled students.
    """
    start_time = time()
    students_to_generate_certs_for = CourseEnrollment.objects.users_enrolled_in(course_id)

    student_set = task_input.get('student_set')
    if student_set == 'all_whitelisted':
        # Generate Certificates for all white listed students.
        students_to_generate_certs_for = students_to_generate_certs_for.filter(
            certificatewhitelist__course_id=course_id,
            certificatewhitelist__whitelist=True
        )

    elif student_set == 'whitelisted_not_generated':
        # Whitelist students who did not get certificates already.
        students_to_generate_certs_for = students_to_generate_certs_for.filter(
            certificatewhitelist__course_id=course_id,
            certificatewhitelist__whitelist=True
        ).exclude(
            generatedcertificate__course_id=course_id,
            generatedcertificate__status__in=CertificateStatuses.PASSED_STATUSES
        )

    elif student_set == "specific_student":
        specific_student_id = task_input.get('specific_student_id')
        students_to_generate_certs_for = students_to_generate_certs_for.filter(id=specific_student_id)

    task_progress = TaskProgress(action_name, students_to_generate_certs_for.count(), start_time)

    current_step = {'step': 'Calculating students already have certificates'}
    task_progress.update_task_state(extra_meta=current_step)

    statuses_to_regenerate = task_input.get('statuses_to_regenerate', [])
    if student_set is not None and not statuses_to_regenerate:
        # We want to skip 'filtering students' only when students are given and statuses to regenerate are not
        students_require_certs = students_to_generate_certs_for
    else:
        students_require_certs = students_require_certificate(
            course_id, students_to_generate_certs_for, statuses_to_regenerate
        )

    if statuses_to_regenerate:
        # Mark existing generated certificates as 'unavailable' before regenerating
        # We need to call this method after "students_require_certificate" otherwise "students_require_certificate"
        # would return no results.
        invalidate_generated_certificates(course_id, students_to_generate_certs_for, statuses_to_regenerate)

    task_progress.skipped = task_progress.total - len(students_require_certs)

    current_step = {'step': 'Generating Certificates'}
    task_progress.update_task_state(extra_meta=current_step)

    course = modulestore().get_course(course_id, depth=0)
    # Generate certificate for each student
    for student in students_require_certs:
        task_progress.attempted += 1
        status = generate_user_certificates(
            student,
            course_id,
            course=course
        )

        if CertificateStatuses.is_passing_status(status):
            task_progress.succeeded += 1
        else:
            task_progress.failed += 1

    return task_progress.update_task_state(extra_meta=current_step)
Example #22
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)
            )
Example #23
0
def generate_students_certificates(_xmodule_instance_args, _entry_id,
                                   course_id, task_input, action_name):
    """
    For a given `course_id`, generate certificates for only students present in 'students' key in task_input
    json column, otherwise generate certificates for all enrolled students.
    """
    start_time = time()
    students_to_generate_certs_for = CourseEnrollment.objects.users_enrolled_in(
        course_id)

    student_set = task_input.get('student_set')
    if student_set == 'all_allowlisted':
        # Generate Certificates for all allowlisted students.
        students_to_generate_certs_for = get_enrolled_allowlisted_users(
            course_id)

    elif student_set == 'allowlisted_not_generated':
        # Allowlisted students who did not yet receive certificates
        students_to_generate_certs_for = get_enrolled_allowlisted_not_passing_users(
            course_id)

    elif student_set == "specific_student":
        specific_student_id = task_input.get('specific_student_id')
        students_to_generate_certs_for = students_to_generate_certs_for.filter(
            id=specific_student_id)

    task_progress = TaskProgress(action_name,
                                 students_to_generate_certs_for.count(),
                                 start_time)

    current_step = {'step': 'Calculating students already have certificates'}
    task_progress.update_task_state(extra_meta=current_step)

    statuses_to_regenerate = task_input.get('statuses_to_regenerate', [])
    if student_set is not None and not statuses_to_regenerate:
        # We want to skip 'filtering students' only when students are given and statuses to regenerate are not
        students_require_certs = students_to_generate_certs_for
    else:
        students_require_certs = students_require_certificate(
            course_id, students_to_generate_certs_for, statuses_to_regenerate)

    log.info(
        f'About to attempt certificate generation for {len(students_require_certs)} users in course {course_id}. '
        f'The student_set is {student_set} and statuses_to_regenerate is {statuses_to_regenerate}'
    )
    if statuses_to_regenerate:
        # Mark existing generated certificates as 'unavailable' before regenerating
        # We need to call this method after "students_require_certificate" otherwise "students_require_certificate"
        # would return no results.
        _invalidate_generated_certificates(course_id,
                                           students_to_generate_certs_for,
                                           statuses_to_regenerate)

    task_progress.skipped = task_progress.total - len(students_require_certs)

    current_step = {'step': 'Generating Certificates'}
    task_progress.update_task_state(extra_meta=current_step)

    # Generate certificate for each student
    for student in students_require_certs:
        task_progress.attempted += 1
        if can_generate_certificate_task(student, course_id):
            log.info(
                f'{course_id} is using V2 certificates. Attempt will be made to generate a V2 certificate '
                f'for user {student.id}.')
            generate_certificate_task(student, course_id)
        else:
            log.info(
                f'Attempt will be made to generate a certificate for user {student.id} in {course_id}.'
            )
            generate_user_certificates(student, course_id)
    return task_progress.update_task_state(extra_meta=current_step)
    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)
            )
    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))
    def handle(self, *args, **options):
        """Resubmit certificates with status 'error'.

        Arguments:
            username (unicode): Identifier for the certificate's user.

        Keyword Arguments:
            course_key_list (list): List of course key strings.

        Raises:
            CommandError

        """
        only_course_keys = []
        for course_key_str in options['course_key_list']:
            try:
                only_course_keys.append(CourseKey.from_string(course_key_str))
            except InvalidKeyError:
                raise CommandError(
                    u'"{course_key_str}" is not a valid course key.'.format(
                        course_key_str=course_key_str
                    )
                )

        if only_course_keys:
            LOGGER.info(
                (
                    u'Starting to re-submit certificates with status "error" '
                    u'in these courses: %s'
                ), ", ".join([text_type(key) for key in only_course_keys])
            )
        else:
            LOGGER.info(u'Starting to re-submit certificates with status "error".')

        # Retrieve the IDs of generated certificates with
        # error status in the set of courses we're considering.
        queryset = (
            GeneratedCertificate.objects.select_related('user')  # pylint: disable=no-member
        ).filter(status=CertificateStatuses.error)
        if only_course_keys:
            queryset = queryset.filter(course_id__in=only_course_keys)

        resubmit_list = [(cert.user, cert.course_id) for cert in queryset]
        course_cache = {}
        resubmit_count = 0
        for user, course_key in resubmit_list:
            course = self._load_course_with_cache(course_key, course_cache)

            if course is not None:
                certs_api.generate_user_certificates(user, course_key, course=course)
                resubmit_count += 1
                LOGGER.info(
                    (
                        u"Re-submitted certificate for user %s "
                        u"in course '%s'"
                    ), user.id, course_key
                )
            else:
                LOGGER.error(
                    (
                        u"Could not find course for course key '%s'.  "
                        u"Certificate for user %s will not be resubmitted."
                    ), course_key, user.id
                )

        LOGGER.info(u"Finished resubmitting %s certificate tasks", resubmit_count)