def test_course_run_certificate_idempotent(user, course): """ Test that the certificate generation is idempotent """ grade = CourseRunGradeFactory.create(course_run__course=course, user=user, grade=0.25, passed=True) # Certificate is created the first time certificate, created, deleted = process_course_run_grade_certificate(grade) assert certificate assert created assert not deleted # Existing certificate is simply returned without any create/delete certificate, created, deleted = process_course_run_grade_certificate(grade) assert certificate assert not created assert not deleted
def test_course_run_certificate_not_passing(user, course): """ Test that the certificate is not generated if the grade is set to 0.0 """ grade = CourseRunGradeFactory.create(course_run__course=course, user=user, grade=1.0, passed=True) # Initially the certificate is created certificate, created, deleted = process_course_run_grade_certificate(grade) assert certificate assert created assert not deleted # Now that the grade indicates score 0.0, certificate should be deleted grade.grade = 0.0 certificate, created, deleted = process_course_run_grade_certificate(grade) assert not certificate assert not created assert deleted
def test_course_run_certificate(user, course, grade, passed, exp_certificate, exp_created, exp_deleted): """ Test that the certificate is generated correctly """ certificate, created, deleted = process_course_run_grade_certificate( CourseRunGradeFactory.create(course_run__course=course, user=user, grade=grade, passed=passed)) assert bool(certificate) is exp_certificate assert created is exp_created assert deleted is exp_deleted
def generate_course_certificates(): """ Task to generate certificates for courses. """ now = now_in_utc() course_runs = (CourseRun.objects.live().filter( end_date__lt=now - timedelta(hours=settings.CERTIFICATE_CREATION_DELAY_IN_HOURS)).exclude( id__in=CourseRunCertificate.objects.values_list("course_run__id", flat=True))) for run in course_runs: edx_grade_user_iter = exception_logging_generator( get_edx_grades_with_users(run)) created_grades_count, updated_grades_count, generated_certificates_count = ( 0, 0, 0, ) for edx_grade, user in edx_grade_user_iter: course_run_grade, created, updated = ensure_course_run_grade( user=user, course_run=run, edx_grade=edx_grade, should_update=True) if created: created_grades_count += 1 elif updated: updated_grades_count += 1 _, created, deleted = process_course_run_grade_certificate( course_run_grade=course_run_grade) if deleted: log.warning( "Certificate deleted for user %s and course_run %s", user, run) elif created: generated_certificates_count += 1 log.info( "Finished processing course run %s: created grades for %d users, " "updated grades for %d users, generated certificates for %d users", run, created_grades_count, updated_grades_count, generated_certificates_count, )
def handle(self, *args, **options): # pylint: disable=too-many-locals,too-many-branches """Handle command execution""" # Grade override for all users for the course run. Disallowed. if options["grade"] is not None and not options["user"]: raise CommandError( "No user supplied with override grade. Overwrite of grade is not supported for all users. Grade should only be supplied when a specific user is targeted." ) try: run = CourseRun.objects.get(courseware_id=options["run"]) except CourseRun.DoesNotExist: raise CommandError( "Could not find run with courseware_id={}".format( options["run"])) now = now_in_utc() if not options.get("force") and (run.end_date is None or run.end_date > now): raise CommandError( "The given course run has not yet finished, so the course grades should not be " "considered final (courseware_id={}, end_date={}).\n" "Add the -f/--force flag if grades/certificates should be synced anyway." .format( options["run"], "None" if run.end_date is None else run.end_date.isoformat(), )) user = fetch_user(options["user"]) if options["user"] else None override_grade = None should_update = options["update"] if options["grade"] is not None: override_grade = float(options["grade"]) if override_grade and (override_grade < 0.0 or override_grade > 1.0): raise CommandError( "Invalid value for grade. Allowed range: 0.0 - 1.0") edx_grade_user_iter = get_edx_grades_with_users(run, user=user) results = [] for edx_grade, user in edx_grade_user_iter: course_run_grade, created_grade, updated_grade = ensure_course_run_grade( user=user, course_run=run, edx_grade=edx_grade, should_update=should_update, ) if override_grade is not None: course_run_grade.grade = override_grade course_run_grade.passed = bool(override_grade) course_run_grade.letter_grade = None course_run_grade.set_by_admin = True course_run_grade.save_and_log(None) _, created_cert, deleted_cert = process_course_run_grade_certificate( course_run_grade=course_run_grade) if created_grade: grade_status = "created" elif updated_grade: grade_status = "updated" else: grade_status = "already exists" grade_summary = ["passed: {}".format(course_run_grade.passed)] if override_grade is not None: grade_summary.append("value override: {}".format( course_run_grade.grade)) if created_cert: cert_status = "created" elif deleted_cert: cert_status = "deleted" elif course_run_grade.passed: cert_status = "already exists" else: cert_status = "ignored" result_summary = "Grade: {} ({}), Certificate: {}".format( grade_status, ", ".join(grade_summary), cert_status) results.append( "Processed user {} ({}) in course run {}. Result - {}".format( user.username, user.email, run.courseware_id, result_summary)) for result in results: self.stdout.write(self.style.SUCCESS(result))