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)
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)
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, ))
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)
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)
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_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
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']}
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
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' )
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}')
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' )
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)
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 )
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 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, ) )
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')
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')
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)
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) )
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)