Skip to content

Commit dbd51b5

Browse files
authored
Fix consideration for program certificate generation (#3597)
1 parent 6b41eba commit dbd51b5

3 files changed

Lines changed: 50 additions & 5 deletions

File tree

courses/api.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,18 +1115,25 @@ def _has_earned_program_cert(user, program):
11151115
bool: True if a user has earned all the course certificates required
11161116
for a given program else False
11171117
"""
1118-
program_course_ids = [course[0].id for course in program.courses]
1118+
1119+
user_courseruns = CourseRun.objects.filter(
1120+
enrollments__user=user,
1121+
enrollments__active=True,
1122+
enrollments__change_status__isnull=True,
1123+
).filter(course__in=program.courses_qset)
11191124

11201125
cert_courses = Course.objects.filter(
1121-
id__in=program_course_ids,
1126+
courseruns__in=user_courseruns,
11221127
courseruns__courseruncertificates__user=user,
11231128
courseruns__courseruncertificates__is_revoked=False,
11241129
)
11251130
grade_courses = Course.objects.filter(
1126-
id__in=program_course_ids,
1131+
courseruns__in=user_courseruns.exclude(
1132+
enrollment_modes__mode_slug=EDX_ENROLLMENT_VERIFIED_MODE
1133+
),
11271134
courseruns__grades__user=user,
11281135
courseruns__grades__passed=True,
1129-
).exclude(courseruns__enrollment_modes__mode_slug=EDX_ENROLLMENT_VERIFIED_MODE)
1136+
)
11301137
root = ProgramRequirement.get_root_nodes().get(program=program)
11311138

11321139
def _has_earned(node):

courses/api_test.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1641,6 +1641,9 @@ def test_generate_program_certificate_success_single_requirement_course(
16411641
course_run = CourseRunFactory.create(course=course)
16421642
course_run.enrollment_modes.set(default_mode_records)
16431643
course_run.save()
1644+
CourseRunEnrollmentFactory.create(
1645+
run=course_run, user=user, enrollment_mode=EDX_ENROLLMENT_VERIFIED_MODE
1646+
)
16441647
CourseRunGradeFactory.create(course_run=course_run, user=user, passed=True, grade=1)
16451648

16461649
CourseRunCertificateFactory.create(user=user, course_run=course_run)
@@ -1686,6 +1689,12 @@ def test_generate_program_certificate_success_multiple_required_courses(
16861689
run.enrollment_modes.set(default_mode_records)
16871690
run.save()
16881691

1692+
CourseRunEnrollmentFactory.create_batch(
1693+
3,
1694+
run=factory.Iterator(course_runs),
1695+
user=user,
1696+
enrollment_mode=EDX_ENROLLMENT_VERIFIED_MODE,
1697+
)
16891698
CourseRunCertificateFactory.create_batch(
16901699
3, user=user, course_run=factory.Iterator(course_runs)
16911700
)
@@ -1995,6 +2004,9 @@ def test_generate_program_certificate_with_subprogram_requirement( # noqa: PLR0
19952004
sub_course_run = CourseRunFactory.create(course=sub_course)
19962005
sub_course_run.enrollment_modes.set(default_mode_records)
19972006
sub_course_run.save()
2007+
CourseRunEnrollmentFactory.create(
2008+
run=sub_course_run, user=user, enrollment_mode=EDX_ENROLLMENT_VERIFIED_MODE
2009+
)
19982010
CourseRunGradeFactory.create(
19992011
course_run=sub_course_run, user=user, passed=True, grade=1
20002012
)
@@ -2065,6 +2077,9 @@ def test_generate_program_certificate_with_revoked_subprogram_certificate(
20652077

20662078
# User completes the sub-program and gets a certificate, but it gets revoked
20672079
sub_course_run = CourseRunFactory.create(course=sub_course)
2080+
CourseRunEnrollmentFactory.create(
2081+
run=sub_course_run, user=user, enrollment_mode=EDX_ENROLLMENT_VERIFIED_MODE
2082+
)
20682083
CourseRunGradeFactory.create(
20692084
course_run=sub_course_run, user=user, passed=True, grade=1
20702085
)
@@ -2122,6 +2137,14 @@ def test_generate_program_certificate_audit_courses(user, default_mode_records):
21222137
program.add_requirement(cert_course)
21232138
program.add_requirement(audit_course)
21242139

2140+
# Add some additional course runs for the audit course.
2141+
# This test missed a case - if the course had a mix of runs that were both
2142+
# audit-only and not, the certificates wouldn't be generated.
2143+
2144+
audit_verified_course_run = CourseRunFactory.create(course=audit_course)
2145+
audit_verified_course_run.enrollment_modes.set(default_mode_records)
2146+
audit_verified_course_run.save()
2147+
21252148
ProgramEnrollment.objects.create(
21262149
user=user,
21272150
program=program,
@@ -3478,6 +3501,9 @@ def test_generate_missing_program_certificates_creates_cert(
34783501
course_run = CourseRunFactory.create(course=course)
34793502
course_run.enrollment_modes.set(default_mode_records)
34803503
course_run.save()
3504+
CourseRunEnrollmentFactory.create(
3505+
run=course_run, user=user, enrollment_mode=EDX_ENROLLMENT_VERIFIED_MODE
3506+
)
34813507

34823508
ProgramEnrollment.objects.create(
34833509
user=user, program=program, enrollment_mode=EDX_ENROLLMENT_VERIFIED_MODE
@@ -3510,6 +3536,9 @@ def test_generate_missing_program_certificates_idempotent(
35103536
course_run = CourseRunFactory.create(course=course)
35113537
course_run.enrollment_modes.set(default_mode_records)
35123538
course_run.save()
3539+
CourseRunEnrollmentFactory.create(
3540+
run=course_run, user=user, enrollment_mode=EDX_ENROLLMENT_VERIFIED_MODE
3541+
)
35133542

35143543
ProgramEnrollment.objects.create(
35153544
user=user, program=program, enrollment_mode=EDX_ENROLLMENT_VERIFIED_MODE

courses/management/tests/manage_program_certificate_test.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,18 @@ def test_program_certificate_management_revoke_unrevoke_success(user, revoke, un
125125
assert certificate.is_revoked is (False if unrevoke else True) # noqa: SIM211
126126

127127

128+
@pytest.mark.parametrize(
129+
"force_cert",
130+
[
131+
True,
132+
False,
133+
],
134+
)
128135
def test_program_certificate_management_create(
129136
user,
130137
program_with_empty_requirements, # noqa: F811
131138
mocker,
139+
force_cert,
132140
):
133141
"""
134142
Test that create operation for program certificate management command
@@ -162,13 +170,14 @@ def test_program_certificate_management_create(
162170
create=True,
163171
program=program_with_empty_requirements.readable_id,
164172
user=user.edx_username,
173+
force=force_cert,
165174
)
166175

167176
generated_certificates = ProgramCertificate.objects.filter(
168177
user=user, program=program_with_empty_requirements
169178
)
170179

171-
assert generated_certificates.count() == 1
180+
assert generated_certificates.count() == (1 if force_cert else 0)
172181

173182

174183
def test_program_certificate_management_force_create(

0 commit comments

Comments
 (0)