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 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"] course_overview = get_course_overview_or_none(course_key) if not course_overview: 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: generate_certificate_task(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)
def test_generation(self): """ Test that a cert is successfully generated """ cert = get_certificate_for_user_id(self.user.id, self.course_run_key) assert not cert with mock.patch(PASSING_GRADE_METHOD, return_value=True): with mock.patch(ID_VERIFIED_METHOD, return_value=True): generate_certificate_task(self.user, self.course_run_key) cert = get_certificate_for_user_id(self.user.id, self.course_run_key) assert cert.status == CertificateStatuses.downloadable assert cert.mode == CourseMode.VERIFIED
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}') 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 log.info(f'Attempt will be made to generate a course certificate for {student.id} : {course_id}.') generate_certificate_task(student, course_id) return task_progress.update_task_state(extra_meta=current_step)
def test_generation_notpassing(self): """ Test that a cert is successfully generated with a status of notpassing """ GeneratedCertificateFactory(user=self.user, course_id=self.course_run_key, status=CertificateStatuses.unavailable, mode=CourseMode.AUDIT) with mock.patch(PASSING_GRADE_METHOD, return_value=False): with mock.patch(ID_VERIFIED_METHOD, return_value=True): generate_certificate_task(self.user, self.course_run_key) cert = get_certificate_for_user_id(self.user.id, self.course_run_key) assert cert.status == CertificateStatuses.notpassing assert cert.mode == CourseMode.VERIFIED
def test_generation_unverified(self, enable_idv_requirement): """ Test that a cert is successfully generated with a status of unverified """ cert = get_certificate_for_user_id(self.user.id, self.course_run_key) assert not cert with mock.patch(PASSING_GRADE_METHOD, return_value=True): with mock.patch(ID_VERIFIED_METHOD, return_value=False): with mock.patch.dict(settings.FEATURES, ENABLE_CERTIFICATES_IDV_REQUIREMENT=enable_idv_requirement): generate_certificate_task(self.user, self.course_run_key) cert = get_certificate_for_user_id(self.user.id, self.course_run_key) assert cert.mode == CourseMode.VERIFIED if enable_idv_requirement: assert cert.status == CertificateStatuses.unverified else: assert cert.status == CertificateStatuses.downloadable
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) 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) course = modulestore().get_course(course_id, depth=0) # 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, course=course) return task_progress.update_task_state(extra_meta=current_step)