Merge pull request #28196 from edx/crice/gen
fix: Retrieve grade and enrollment mode only once
This commit is contained in:
@@ -47,10 +47,13 @@ def generate_allowlist_certificate_task(user, course_key, generation_mode=None):
|
||||
"""
|
||||
Create a task to generate an allowlist certificate for this user in this course run.
|
||||
"""
|
||||
if _can_generate_allowlist_certificate(user, course_key):
|
||||
return _generate_certificate_task(user=user, course_key=course_key, generation_mode=generation_mode)
|
||||
enrollment_mode = _get_enrollment_mode(user, course_key)
|
||||
course_grade = _get_course_grade(user, course_key)
|
||||
if _can_generate_allowlist_certificate(user, course_key, enrollment_mode):
|
||||
return _generate_certificate_task(user=user, course_key=course_key, enrollment_mode=enrollment_mode,
|
||||
course_grade=course_grade, generation_mode=generation_mode)
|
||||
|
||||
status = _set_allowlist_cert_status(user, course_key)
|
||||
status = _set_allowlist_cert_status(user, course_key, enrollment_mode, course_grade)
|
||||
if status is not None:
|
||||
return True
|
||||
|
||||
@@ -62,25 +65,32 @@ def _generate_regular_certificate_task(user, course_key, generation_mode=None):
|
||||
Create a task to generate a regular (non-allowlist) certificate for this user in this course run, if the user is
|
||||
eligible and a certificate can be generated.
|
||||
"""
|
||||
if _can_generate_regular_certificate(user, course_key):
|
||||
return _generate_certificate_task(user=user, course_key=course_key, generation_mode=generation_mode)
|
||||
enrollment_mode = _get_enrollment_mode(user, course_key)
|
||||
course_grade = _get_course_grade(user, course_key)
|
||||
if _can_generate_regular_certificate(user, course_key, enrollment_mode, course_grade):
|
||||
return _generate_certificate_task(user=user, course_key=course_key, enrollment_mode=enrollment_mode,
|
||||
course_grade=course_grade, generation_mode=generation_mode)
|
||||
|
||||
status = _set_regular_cert_status(user, course_key)
|
||||
status = _set_regular_cert_status(user, course_key, enrollment_mode, course_grade)
|
||||
if status is not None:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def _generate_certificate_task(user, course_key, status=None, generation_mode=None):
|
||||
def _generate_certificate_task(user, course_key, enrollment_mode, course_grade, status=None, generation_mode=None):
|
||||
"""
|
||||
Create a task to generate a certificate
|
||||
"""
|
||||
log.info(f'About to create a regular certificate task for {user.id} : {course_key}')
|
||||
|
||||
course_grade_val = _get_grade_value(course_grade)
|
||||
|
||||
kwargs = {
|
||||
'student': str(user.id),
|
||||
'course_key': str(course_key)
|
||||
'course_key': str(course_key),
|
||||
'enrollment_mode': str(enrollment_mode),
|
||||
'course_grade': str(course_grade_val)
|
||||
}
|
||||
if status is not None:
|
||||
kwargs['status'] = status
|
||||
@@ -91,7 +101,7 @@ def _generate_certificate_task(user, course_key, status=None, generation_mode=No
|
||||
return True
|
||||
|
||||
|
||||
def _can_generate_allowlist_certificate(user, course_key):
|
||||
def _can_generate_allowlist_certificate(user, course_key, enrollment_mode):
|
||||
"""
|
||||
Check if an allowlist certificate can be generated (created if it doesn't already exist, or updated if it does
|
||||
exist) for this user, in this course run.
|
||||
@@ -103,7 +113,7 @@ def _can_generate_allowlist_certificate(user, course_key):
|
||||
|
||||
log.info(f'{user.id} : {course_key} is on the certificate allowlist')
|
||||
|
||||
if not _can_generate_certificate_common(user, course_key):
|
||||
if not _can_generate_certificate_common(user, course_key, enrollment_mode):
|
||||
log.info(f'One of the common checks failed. Allowlist certificate cannot be generated for {user.id} : '
|
||||
f'{course_key}.')
|
||||
return False
|
||||
@@ -112,7 +122,7 @@ def _can_generate_allowlist_certificate(user, course_key):
|
||||
return True
|
||||
|
||||
|
||||
def _can_generate_regular_certificate(user, course_key):
|
||||
def _can_generate_regular_certificate(user, course_key, enrollment_mode, course_grade):
|
||||
"""
|
||||
Check if a regular (non-allowlist) course certificate can be generated (created if it doesn't already exist, or
|
||||
updated if it does exist) for this user, in this course run.
|
||||
@@ -125,11 +135,11 @@ def _can_generate_regular_certificate(user, course_key):
|
||||
log.info(f'{user.id} is a beta tester in {course_key}. Certificate cannot be generated.')
|
||||
return False
|
||||
|
||||
if not _has_passing_grade(user, course_key):
|
||||
if not _is_passing_grade(course_grade):
|
||||
log.info(f'{user.id} does not have a passing grade in {course_key}. Certificate cannot be generated.')
|
||||
return False
|
||||
|
||||
if not _can_generate_certificate_common(user, course_key):
|
||||
if not _can_generate_certificate_common(user, course_key, enrollment_mode):
|
||||
log.info(f'One of the common checks failed. Certificate cannot be generated for {user.id} : {course_key}.')
|
||||
return False
|
||||
|
||||
@@ -137,7 +147,7 @@ def _can_generate_regular_certificate(user, course_key):
|
||||
return True
|
||||
|
||||
|
||||
def _can_generate_certificate_common(user, course_key):
|
||||
def _can_generate_certificate_common(user, course_key, enrollment_mode):
|
||||
"""
|
||||
Check if a course certificate can be generated (created if it doesn't already exist, or updated if it does
|
||||
exist) for this user, in this course run.
|
||||
@@ -149,7 +159,6 @@ def _can_generate_certificate_common(user, course_key):
|
||||
log.info(f'{user.id} : {course_key} is on the certificate invalidation list. Certificate cannot be generated.')
|
||||
return False
|
||||
|
||||
enrollment_mode, __ = CourseEnrollment.enrollment_mode_for_user(user, course_key)
|
||||
if enrollment_mode is None:
|
||||
log.info(f'{user.id} : {course_key} does not have an enrollment. Certificate cannot be generated.')
|
||||
return False
|
||||
@@ -179,45 +188,45 @@ def _can_generate_certificate_common(user, course_key):
|
||||
return True
|
||||
|
||||
|
||||
def _set_allowlist_cert_status(user, course_key):
|
||||
def _set_allowlist_cert_status(user, course_key, enrollment_mode, course_grade):
|
||||
"""
|
||||
Determine the allowlist certificate status for this user, in this course run and update the cert.
|
||||
|
||||
This is used when a downloadable cert cannot be generated, but we want to provide more info about why it cannot
|
||||
be generated.
|
||||
"""
|
||||
if not _can_set_allowlist_cert_status(user, course_key):
|
||||
if not _can_set_allowlist_cert_status(user, course_key, enrollment_mode):
|
||||
return None
|
||||
|
||||
cert = GeneratedCertificate.certificate_for_student(user, course_key)
|
||||
return _get_cert_status_common(user, course_key, cert)
|
||||
return _get_cert_status_common(user, course_key, enrollment_mode, course_grade, cert)
|
||||
|
||||
|
||||
def _set_regular_cert_status(user, course_key):
|
||||
def _set_regular_cert_status(user, course_key, enrollment_mode, course_grade):
|
||||
"""
|
||||
Determine the regular (non-allowlist) certificate status for this user, in this course run.
|
||||
|
||||
This is used when a downloadable cert cannot be generated, but we want to provide more info about why it cannot
|
||||
be generated.
|
||||
"""
|
||||
if not _can_set_regular_cert_status(user, course_key):
|
||||
if not _can_set_regular_cert_status(user, course_key, enrollment_mode):
|
||||
return None
|
||||
|
||||
cert = GeneratedCertificate.certificate_for_student(user, course_key)
|
||||
status = _get_cert_status_common(user, course_key, cert)
|
||||
status = _get_cert_status_common(user, course_key, enrollment_mode, course_grade, cert)
|
||||
if status is not None:
|
||||
return status
|
||||
|
||||
if IDVerificationService.user_is_verified(user) and not _has_passing_grade(user, course_key) and cert is not None:
|
||||
if IDVerificationService.user_is_verified(user) and not _is_passing_grade(course_grade) and cert is not None:
|
||||
if cert.status != CertificateStatuses.notpassing:
|
||||
course_grade = _get_course_grade(user, course_key)
|
||||
cert.mark_notpassing(course_grade.percent, source='certificate_generation')
|
||||
course_grade_val = _get_grade_value(course_grade)
|
||||
cert.mark_notpassing(course_grade_val, source='certificate_generation')
|
||||
return CertificateStatuses.notpassing
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def _get_cert_status_common(user, course_key, cert):
|
||||
def _get_cert_status_common(user, course_key, enrollment_mode, course_grade, cert):
|
||||
"""
|
||||
Determine the certificate status for this user, in this course run.
|
||||
|
||||
@@ -229,10 +238,12 @@ def _get_cert_status_common(user, course_key, cert):
|
||||
cert.invalidate(source='certificate_generation')
|
||||
return CertificateStatuses.unavailable
|
||||
|
||||
if not IDVerificationService.user_is_verified(user) and _has_passing_grade_or_is_allowlisted(user, course_key):
|
||||
if not IDVerificationService.user_is_verified(user) and _has_passing_grade_or_is_allowlisted(user, course_key,
|
||||
course_grade):
|
||||
if cert is None:
|
||||
_generate_certificate_task(user=user, course_key=course_key, generation_mode='batch',
|
||||
status=CertificateStatuses.unverified)
|
||||
_generate_certificate_task(user=user, course_key=course_key, enrollment_mode=enrollment_mode,
|
||||
course_grade=course_grade, status=CertificateStatuses.unverified,
|
||||
generation_mode='batch')
|
||||
elif cert.status != CertificateStatuses.unverified:
|
||||
cert.mark_unverified(source='certificate_generation')
|
||||
return CertificateStatuses.unverified
|
||||
@@ -240,17 +251,17 @@ def _get_cert_status_common(user, course_key, cert):
|
||||
return None
|
||||
|
||||
|
||||
def _can_set_allowlist_cert_status(user, course_key):
|
||||
def _can_set_allowlist_cert_status(user, course_key, enrollment_mode):
|
||||
"""
|
||||
Determine whether we can set a custom (non-downloadable) cert status for an allowlist certificate
|
||||
"""
|
||||
if not is_on_certificate_allowlist(user, course_key):
|
||||
return False
|
||||
|
||||
return _can_set_cert_status_common(user, course_key)
|
||||
return _can_set_cert_status_common(user, course_key, enrollment_mode)
|
||||
|
||||
|
||||
def _can_set_regular_cert_status(user, course_key):
|
||||
def _can_set_regular_cert_status(user, course_key, enrollment_mode):
|
||||
"""
|
||||
Determine whether we can set a custom (non-downloadable) cert status for a regular (non-allowlist) certificate
|
||||
"""
|
||||
@@ -260,17 +271,16 @@ def _can_set_regular_cert_status(user, course_key):
|
||||
if _is_beta_tester(user, course_key):
|
||||
return False
|
||||
|
||||
return _can_set_cert_status_common(user, course_key)
|
||||
return _can_set_cert_status_common(user, course_key, enrollment_mode)
|
||||
|
||||
|
||||
def _can_set_cert_status_common(user, course_key):
|
||||
def _can_set_cert_status_common(user, course_key, enrollment_mode):
|
||||
"""
|
||||
Determine whether we can set a custom (non-downloadable) cert status
|
||||
"""
|
||||
if _is_cert_downloadable(user, course_key):
|
||||
return False
|
||||
|
||||
enrollment_mode, __ = CourseEnrollment.enrollment_mode_for_user(user, course_key)
|
||||
if enrollment_mode is None:
|
||||
return False
|
||||
|
||||
@@ -329,7 +339,7 @@ def _is_ccx_course(course_key):
|
||||
return hasattr(course_key, 'ccx')
|
||||
|
||||
|
||||
def _has_passing_grade_or_is_allowlisted(user, course_key):
|
||||
def _has_passing_grade_or_is_allowlisted(user, course_key, course_grade):
|
||||
"""
|
||||
Check if the user has a passing grade in this course run, or is on the allowlist and so is exempt from needing
|
||||
a passing grade.
|
||||
@@ -337,24 +347,42 @@ def _has_passing_grade_or_is_allowlisted(user, course_key):
|
||||
if is_on_certificate_allowlist(user, course_key):
|
||||
return True
|
||||
|
||||
return _has_passing_grade(user, course_key)
|
||||
return _is_passing_grade(course_grade)
|
||||
|
||||
|
||||
def _has_passing_grade(user, course_key):
|
||||
def _is_passing_grade(course_grade):
|
||||
"""
|
||||
Check if the user has a passing grade in this course run
|
||||
Check if the grade is a passing grade
|
||||
"""
|
||||
course_grade = _get_course_grade(user, course_key)
|
||||
return course_grade.passed
|
||||
if course_grade:
|
||||
return course_grade.passed
|
||||
return False
|
||||
|
||||
|
||||
def _get_grade_value(course_grade):
|
||||
"""
|
||||
Get the user's course grade as a percent, or an empty string if there is no grade
|
||||
"""
|
||||
if course_grade:
|
||||
return course_grade.percent
|
||||
return ''
|
||||
|
||||
|
||||
def _get_course_grade(user, course_key):
|
||||
"""
|
||||
Get the user's course grade in this course run
|
||||
Get the user's course grade in this course run. Note that this may be None.
|
||||
"""
|
||||
return CourseGradeFactory().read(user, course_key=course_key)
|
||||
|
||||
|
||||
def _get_enrollment_mode(user, course_key):
|
||||
"""
|
||||
Get the user's enrollment mode for this course run. Note that this may be None.
|
||||
"""
|
||||
enrollment_mode, __ = CourseEnrollment.enrollment_mode_for_user(user, course_key)
|
||||
return enrollment_mode
|
||||
|
||||
|
||||
def _is_cert_downloadable(user, course_key):
|
||||
"""
|
||||
Check if cert already exists, has a downloadable status, and has not been invalidated
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import uuid
|
||||
from contextlib import contextmanager
|
||||
from datetime import datetime, timedelta
|
||||
from unittest import mock
|
||||
from unittest.mock import patch
|
||||
|
||||
import ddt
|
||||
@@ -23,7 +24,6 @@ from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
from common.djangoapps.course_modes.models import CourseMode
|
||||
from common.djangoapps.course_modes.tests.factories import CourseModeFactory
|
||||
from common.djangoapps.student.models import CourseEnrollment
|
||||
from common.djangoapps.student.tests.factories import (
|
||||
CourseEnrollmentFactory,
|
||||
GlobalStaffFactory,
|
||||
@@ -37,6 +37,7 @@ from lms.djangoapps.certificates.api import (
|
||||
create_certificate_invalidation_entry,
|
||||
create_or_update_certificate_allowlist_entry,
|
||||
example_certificates_status,
|
||||
generate_certificate_task,
|
||||
generate_example_certificates,
|
||||
get_allowlist_entry,
|
||||
get_allowlisted_users,
|
||||
@@ -66,6 +67,7 @@ from lms.djangoapps.certificates.tests.factories import (
|
||||
GeneratedCertificateFactory,
|
||||
CertificateInvalidationFactory
|
||||
)
|
||||
from lms.djangoapps.certificates.tests.test_generation_handler import ID_VERIFIED_METHOD, PASSING_GRADE_METHOD
|
||||
from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory
|
||||
from openedx.core.djangoapps.site_configuration.tests.test_util import with_site_configuration
|
||||
|
||||
@@ -536,39 +538,46 @@ class CertificateGetTests(SharedModuleStoreTestCase):
|
||||
assert get_certificate_for_user(self.student.username, self.nonexistent_course_id) is None
|
||||
|
||||
|
||||
@override_settings(CERT_QUEUE='certificates')
|
||||
class GenerateUserCertificatesTest(EventTestMixin, WebCertificateTestMixin, ModuleStoreTestCase):
|
||||
class GenerateUserCertificatesTest(ModuleStoreTestCase):
|
||||
"""Tests for generating certificates for students. """
|
||||
|
||||
ERROR_REASON = "Kaboom!"
|
||||
ENABLED_SIGNALS = ['course_published']
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
def setUp(self): # pylint: disable=arguments-differ
|
||||
super().setUp('lms.djangoapps.certificates.utils.tracker')
|
||||
|
||||
self.student = UserFactory.create(
|
||||
email='joe_user@edx.org',
|
||||
username='joeuser',
|
||||
password='foo'
|
||||
self.user = UserFactory()
|
||||
self.course_run = CourseFactory()
|
||||
self.course_run_key = self.course_run.id # pylint: disable=no-member
|
||||
self.enrollment = CourseEnrollmentFactory(
|
||||
user=self.user,
|
||||
course_id=self.course_run_key,
|
||||
is_active=True,
|
||||
mode=CourseMode.VERIFIED,
|
||||
)
|
||||
self.student_no_cert = UserFactory()
|
||||
self.course = CourseFactory.create(
|
||||
org='edx',
|
||||
number='verified',
|
||||
display_name='Verified Course',
|
||||
grade_cutoffs={'cutoff': 0.75, 'Pass': 0.5}
|
||||
)
|
||||
self.enrollment = CourseEnrollment.enroll(self.student, self.course.id, mode='honor')
|
||||
self.request_factory = RequestFactory()
|
||||
|
||||
@patch.dict(settings.FEATURES, {'CERTIFICATES_HTML_VIEW': False})
|
||||
def test_cert_url_empty_with_invalid_certificate(self):
|
||||
"""
|
||||
Test certificate url is empty if html view is not enabled and certificate is not yet generated
|
||||
"""
|
||||
url = get_certificate_url(self.student.id, self.course.id)
|
||||
url = get_certificate_url(self.user.id, self.course_run_key)
|
||||
assert url == ''
|
||||
|
||||
@patch.dict(settings.FEATURES, {'CERTIFICATES_HTML_VIEW': True})
|
||||
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
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class CertificateGenerationEnabledTest(EventTestMixin, TestCase):
|
||||
|
||||
@@ -13,12 +13,12 @@ from lms.djangoapps.certificates.generation_handler import (
|
||||
_can_generate_allowlist_certificate,
|
||||
_can_generate_certificate_for_status,
|
||||
_can_generate_regular_certificate,
|
||||
_generate_regular_certificate_task,
|
||||
_set_allowlist_cert_status,
|
||||
_set_regular_cert_status,
|
||||
generate_allowlist_certificate_task,
|
||||
generate_certificate_task,
|
||||
_generate_regular_certificate_task,
|
||||
is_on_certificate_allowlist,
|
||||
_set_allowlist_cert_status,
|
||||
_set_regular_cert_status
|
||||
is_on_certificate_allowlist
|
||||
)
|
||||
from lms.djangoapps.certificates.models import GeneratedCertificate
|
||||
from lms.djangoapps.certificates.tests.factories import (
|
||||
@@ -26,6 +26,7 @@ from lms.djangoapps.certificates.tests.factories import (
|
||||
CertificateInvalidationFactory,
|
||||
GeneratedCertificateFactory
|
||||
)
|
||||
from lms.djangoapps.grades.api import CourseGradeFactory
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
@@ -34,8 +35,9 @@ log = logging.getLogger(__name__)
|
||||
BETA_TESTER_METHOD = 'lms.djangoapps.certificates.generation_handler._is_beta_tester'
|
||||
COURSE_OVERVIEW_METHOD = 'lms.djangoapps.certificates.generation_handler.get_course_overview_or_none'
|
||||
CCX_COURSE_METHOD = 'lms.djangoapps.certificates.generation_handler._is_ccx_course'
|
||||
GET_GRADE_METHOD = 'lms.djangoapps.certificates.generation_handler._get_course_grade'
|
||||
ID_VERIFIED_METHOD = 'lms.djangoapps.verify_student.services.IDVerificationService.user_is_verified'
|
||||
PASSING_GRADE_METHOD = 'lms.djangoapps.certificates.generation_handler._has_passing_grade'
|
||||
PASSING_GRADE_METHOD = 'lms.djangoapps.certificates.generation_handler._is_passing_grade'
|
||||
WEB_CERTS_METHOD = 'lms.djangoapps.certificates.generation_handler.has_html_certificates_enabled'
|
||||
|
||||
|
||||
@@ -54,11 +56,13 @@ class AllowlistTests(ModuleStoreTestCase):
|
||||
self.user = UserFactory()
|
||||
self.course_run = CourseFactory()
|
||||
self.course_run_key = self.course_run.id # pylint: disable=no-member
|
||||
self.enrollment_mode = CourseMode.VERIFIED
|
||||
self.grade = CourseGradeFactory().read(self.user, self.course_run)
|
||||
self.enrollment = CourseEnrollmentFactory(
|
||||
user=self.user,
|
||||
course_id=self.course_run_key,
|
||||
is_active=True,
|
||||
mode=CourseMode.VERIFIED,
|
||||
mode=self.enrollment_mode,
|
||||
)
|
||||
|
||||
# Add user to the allowlist
|
||||
@@ -177,16 +181,16 @@ class AllowlistTests(ModuleStoreTestCase):
|
||||
"""
|
||||
u = UserFactory()
|
||||
|
||||
assert not _can_generate_allowlist_certificate(u, self.course_run_key)
|
||||
assert not _can_generate_allowlist_certificate(u, self.course_run_key, self.enrollment_mode)
|
||||
assert not generate_allowlist_certificate_task(u, self.course_run_key)
|
||||
assert not generate_certificate_task(u, self.course_run_key)
|
||||
assert _set_allowlist_cert_status(u, self.course_run_key) is None
|
||||
assert _set_allowlist_cert_status(u, self.course_run_key, self.enrollment_mode, self.grade) is None
|
||||
|
||||
def test_handle_valid(self):
|
||||
"""
|
||||
Test handling of a valid user/course run combo
|
||||
"""
|
||||
assert _can_generate_allowlist_certificate(self.user, self.course_run_key)
|
||||
assert _can_generate_allowlist_certificate(self.user, self.course_run_key, self.enrollment_mode)
|
||||
assert generate_allowlist_certificate_task(self.user, self.course_run_key)
|
||||
|
||||
def test_handle_valid_general_methods(self):
|
||||
@@ -200,8 +204,9 @@ class AllowlistTests(ModuleStoreTestCase):
|
||||
Test handling when the user's id is not verified
|
||||
"""
|
||||
with mock.patch(ID_VERIFIED_METHOD, return_value=False):
|
||||
assert not _can_generate_allowlist_certificate(self.user, self.course_run_key)
|
||||
assert _set_allowlist_cert_status(self.user, self.course_run_key) == CertificateStatuses.unverified
|
||||
assert not _can_generate_allowlist_certificate(self.user, self.course_run_key, self.enrollment_mode)
|
||||
assert _set_allowlist_cert_status(self.user, self.course_run_key, self.enrollment_mode, self.grade) == \
|
||||
CertificateStatuses.unverified
|
||||
|
||||
def test_can_generate_not_enrolled(self):
|
||||
"""
|
||||
@@ -210,9 +215,11 @@ class AllowlistTests(ModuleStoreTestCase):
|
||||
u = UserFactory()
|
||||
cr = CourseFactory()
|
||||
key = cr.id # pylint: disable=no-member
|
||||
mode = None
|
||||
grade = None
|
||||
CertificateAllowlistFactory.create(course_id=key, user=u)
|
||||
assert not _can_generate_allowlist_certificate(u, key)
|
||||
assert _set_allowlist_cert_status(u, key) is None
|
||||
assert not _can_generate_allowlist_certificate(u, key, mode)
|
||||
assert _set_allowlist_cert_status(u, key, mode, grade) is None
|
||||
|
||||
def test_can_generate_audit(self):
|
||||
"""
|
||||
@@ -221,16 +228,17 @@ class AllowlistTests(ModuleStoreTestCase):
|
||||
u = UserFactory()
|
||||
cr = CourseFactory()
|
||||
key = cr.id # pylint: disable=no-member
|
||||
mode = CourseMode.AUDIT
|
||||
CourseEnrollmentFactory(
|
||||
user=u,
|
||||
course_id=key,
|
||||
is_active=True,
|
||||
mode=GeneratedCertificate.MODES.audit,
|
||||
mode=mode,
|
||||
)
|
||||
CertificateAllowlistFactory.create(course_id=key, user=u)
|
||||
|
||||
assert not _can_generate_allowlist_certificate(u, key)
|
||||
assert _set_allowlist_cert_status(u, key) is None
|
||||
assert not _can_generate_allowlist_certificate(u, key, mode)
|
||||
assert _set_allowlist_cert_status(u, key, mode, self.grade) is None
|
||||
|
||||
def test_can_generate_not_allowlisted(self):
|
||||
"""
|
||||
@@ -245,8 +253,8 @@ class AllowlistTests(ModuleStoreTestCase):
|
||||
is_active=True,
|
||||
mode=CourseMode.VERIFIED,
|
||||
)
|
||||
assert not _can_generate_allowlist_certificate(u, key)
|
||||
assert _set_allowlist_cert_status(u, key) is None
|
||||
assert not _can_generate_allowlist_certificate(u, key, self.enrollment_mode)
|
||||
assert _set_allowlist_cert_status(u, key, self.enrollment_mode, self.grade) is None
|
||||
|
||||
def test_can_generate_invalidated(self):
|
||||
"""
|
||||
@@ -274,24 +282,24 @@ class AllowlistTests(ModuleStoreTestCase):
|
||||
active=True
|
||||
)
|
||||
|
||||
assert not _can_generate_allowlist_certificate(u, key)
|
||||
assert _set_allowlist_cert_status(u, key) == CertificateStatuses.unavailable
|
||||
assert not _can_generate_allowlist_certificate(u, key, self.enrollment_mode)
|
||||
assert _set_allowlist_cert_status(u, key, self.enrollment_mode, self.grade) == CertificateStatuses.unavailable
|
||||
|
||||
def test_can_generate_web_cert_disabled(self):
|
||||
"""
|
||||
Test handling when web certs are not enabled
|
||||
"""
|
||||
with mock.patch(WEB_CERTS_METHOD, return_value=False):
|
||||
assert not _can_generate_allowlist_certificate(self.user, self.course_run_key)
|
||||
assert _set_allowlist_cert_status(self.user, self.course_run_key) is None
|
||||
assert not _can_generate_allowlist_certificate(self.user, self.course_run_key, self.enrollment_mode)
|
||||
assert _set_allowlist_cert_status(self.user, self.course_run_key, self.enrollment_mode, self.grade) is None
|
||||
|
||||
def test_can_generate_no_overview(self):
|
||||
"""
|
||||
Test handling when the course overview is missing
|
||||
"""
|
||||
with mock.patch(COURSE_OVERVIEW_METHOD, return_value=None):
|
||||
assert not _can_generate_allowlist_certificate(self.user, self.course_run_key)
|
||||
assert _set_allowlist_cert_status(self.user, self.course_run_key) is None
|
||||
assert not _can_generate_allowlist_certificate(self.user, self.course_run_key, self.enrollment_mode)
|
||||
assert _set_allowlist_cert_status(self.user, self.course_run_key, self.enrollment_mode, self.grade) is None
|
||||
|
||||
def test_cert_status_downloadable(self):
|
||||
"""
|
||||
@@ -313,7 +321,7 @@ class AllowlistTests(ModuleStoreTestCase):
|
||||
status=CertificateStatuses.downloadable
|
||||
)
|
||||
|
||||
assert _set_allowlist_cert_status(u, key) is None
|
||||
assert _set_allowlist_cert_status(u, key, self.enrollment_mode, self.grade) is None
|
||||
|
||||
|
||||
@mock.patch(ID_VERIFIED_METHOD, mock.Mock(return_value=True))
|
||||
@@ -333,18 +341,20 @@ class CertificateTests(ModuleStoreTestCase):
|
||||
self.user = UserFactory()
|
||||
self.course_run = CourseFactory()
|
||||
self.course_run_key = self.course_run.id # pylint: disable=no-member
|
||||
self.enrollment_mode = CourseMode.VERIFIED
|
||||
self.grade = CourseGradeFactory().read(self.user, self.course_run)
|
||||
self.enrollment = CourseEnrollmentFactory(
|
||||
user=self.user,
|
||||
course_id=self.course_run_key,
|
||||
is_active=True,
|
||||
mode=CourseMode.VERIFIED,
|
||||
mode=self.enrollment_mode,
|
||||
)
|
||||
|
||||
def test_handle_valid(self):
|
||||
"""
|
||||
Test handling of a valid user/course run combo.
|
||||
"""
|
||||
assert _can_generate_regular_certificate(self.user, self.course_run_key)
|
||||
assert _can_generate_regular_certificate(self.user, self.course_run_key, self.enrollment_mode, self.grade)
|
||||
assert generate_certificate_task(self.user, self.course_run_key)
|
||||
|
||||
def test_handle_valid_task(self):
|
||||
@@ -361,23 +371,33 @@ class CertificateTests(ModuleStoreTestCase):
|
||||
Test handling of an invalid user/course run combo
|
||||
"""
|
||||
other_user = UserFactory()
|
||||
assert not _can_generate_regular_certificate(other_user, self.course_run_key)
|
||||
mode = None
|
||||
grade = None
|
||||
assert not _can_generate_regular_certificate(other_user, self.course_run_key, mode, grade)
|
||||
assert not generate_certificate_task(other_user, self.course_run_key)
|
||||
assert not _generate_regular_certificate_task(other_user, self.course_run_key)
|
||||
|
||||
def test_handle_no_grade(self):
|
||||
"""
|
||||
Test handling when the grade is none
|
||||
"""
|
||||
with mock.patch(GET_GRADE_METHOD, return_value=None):
|
||||
assert generate_certificate_task(self.user, self.course_run_key)
|
||||
|
||||
def test_handle_audit_status(self):
|
||||
"""
|
||||
Test handling of a user who is not passing and is enrolled in audit mode
|
||||
"""
|
||||
different_user = UserFactory()
|
||||
mode = CourseMode.AUDIT
|
||||
CourseEnrollmentFactory(
|
||||
user=different_user,
|
||||
course_id=self.course_run_key,
|
||||
is_active=True,
|
||||
mode=GeneratedCertificate.MODES.audit,
|
||||
mode=mode,
|
||||
)
|
||||
|
||||
assert _set_regular_cert_status(different_user, self.course_run_key) is None
|
||||
assert _set_regular_cert_status(different_user, self.course_run_key, mode, self.grade) is None
|
||||
assert not _generate_regular_certificate_task(different_user, self.course_run_key)
|
||||
|
||||
def test_handle_not_passing_id_verified_no_cert(self):
|
||||
@@ -393,7 +413,8 @@ class CertificateTests(ModuleStoreTestCase):
|
||||
)
|
||||
|
||||
with mock.patch(PASSING_GRADE_METHOD, return_value=False):
|
||||
assert _set_regular_cert_status(different_user, self.course_run_key) is None
|
||||
assert _set_regular_cert_status(different_user, self.course_run_key, self.enrollment_mode,
|
||||
self.grade) is None
|
||||
assert not _generate_regular_certificate_task(different_user, self.course_run_key)
|
||||
|
||||
def test_handle_not_passing_id_verified_cert(self):
|
||||
@@ -415,9 +436,11 @@ class CertificateTests(ModuleStoreTestCase):
|
||||
)
|
||||
|
||||
with mock.patch(PASSING_GRADE_METHOD, return_value=False):
|
||||
assert _set_regular_cert_status(different_user, self.course_run_key) == CertificateStatuses.notpassing
|
||||
assert _set_regular_cert_status(different_user, self.course_run_key, self.enrollment_mode, self.grade) == \
|
||||
CertificateStatuses.notpassing
|
||||
assert _generate_regular_certificate_task(different_user, self.course_run_key) is True
|
||||
assert not _can_generate_regular_certificate(different_user, self.course_run_key)
|
||||
assert not _can_generate_regular_certificate(different_user, self.course_run_key, self.enrollment_mode,
|
||||
self.grade)
|
||||
|
||||
@ddt.data(
|
||||
(CertificateStatuses.deleted, True),
|
||||
@@ -476,8 +499,9 @@ class CertificateTests(ModuleStoreTestCase):
|
||||
)
|
||||
|
||||
with mock.patch(ID_VERIFIED_METHOD, return_value=False):
|
||||
assert not _can_generate_regular_certificate(u, self.course_run_key)
|
||||
assert _set_regular_cert_status(u, self.course_run_key) == CertificateStatuses.unverified
|
||||
assert not _can_generate_regular_certificate(u, self.course_run_key, self.enrollment_mode, self.grade)
|
||||
assert _set_regular_cert_status(u, self.course_run_key, self.enrollment_mode,
|
||||
self.grade) == CertificateStatuses.unverified
|
||||
|
||||
def test_can_generate_not_verified_no_cert(self):
|
||||
"""
|
||||
@@ -492,8 +516,9 @@ class CertificateTests(ModuleStoreTestCase):
|
||||
)
|
||||
|
||||
with mock.patch(ID_VERIFIED_METHOD, return_value=False):
|
||||
assert not _can_generate_regular_certificate(u, self.course_run_key)
|
||||
assert _set_regular_cert_status(u, self.course_run_key) == CertificateStatuses.unverified
|
||||
assert not _can_generate_regular_certificate(u, self.course_run_key, self.enrollment_mode, self.grade)
|
||||
assert _set_regular_cert_status(u, self.course_run_key, self.enrollment_mode,
|
||||
self.grade) == CertificateStatuses.unverified
|
||||
|
||||
def test_can_generate_not_verified_not_passing(self):
|
||||
"""
|
||||
@@ -515,8 +540,8 @@ class CertificateTests(ModuleStoreTestCase):
|
||||
|
||||
with mock.patch(ID_VERIFIED_METHOD, return_value=False):
|
||||
with mock.patch(PASSING_GRADE_METHOD, return_value=False):
|
||||
assert not _can_generate_regular_certificate(u, self.course_run_key)
|
||||
assert _set_regular_cert_status(u, self.course_run_key) is None
|
||||
assert not _can_generate_regular_certificate(u, self.course_run_key, self.enrollment_mode, self.grade)
|
||||
assert _set_regular_cert_status(u, self.course_run_key, self.enrollment_mode, self.grade) is None
|
||||
|
||||
def test_can_generate_not_verified_not_passing_allowlist(self):
|
||||
"""
|
||||
@@ -539,32 +564,36 @@ class CertificateTests(ModuleStoreTestCase):
|
||||
|
||||
with mock.patch(ID_VERIFIED_METHOD, return_value=False):
|
||||
with mock.patch(PASSING_GRADE_METHOD, return_value=False):
|
||||
assert not _can_generate_regular_certificate(u, self.course_run_key)
|
||||
assert _set_regular_cert_status(u, self.course_run_key) == CertificateStatuses.unverified
|
||||
assert not _can_generate_regular_certificate(u, self.course_run_key, self.enrollment_mode, self.grade)
|
||||
assert _set_regular_cert_status(u, self.course_run_key, self.enrollment_mode,
|
||||
self.grade) == CertificateStatuses.unverified
|
||||
|
||||
def test_can_generate_ccx(self):
|
||||
"""
|
||||
Test handling when the course is a CCX (custom edX) course
|
||||
"""
|
||||
with mock.patch(CCX_COURSE_METHOD, return_value=True):
|
||||
assert not _can_generate_regular_certificate(self.user, self.course_run_key)
|
||||
assert _set_regular_cert_status(self.user, self.course_run_key) is None
|
||||
assert not _can_generate_regular_certificate(self.user, self.course_run_key, self.enrollment_mode,
|
||||
self.grade)
|
||||
assert _set_regular_cert_status(self.user, self.course_run_key, self.enrollment_mode, self.grade) is None
|
||||
|
||||
def test_can_generate_beta_tester(self):
|
||||
"""
|
||||
Test handling when the user is a beta tester
|
||||
"""
|
||||
with mock.patch(BETA_TESTER_METHOD, return_value=True):
|
||||
assert not _can_generate_regular_certificate(self.user, self.course_run_key)
|
||||
assert _set_regular_cert_status(self.user, self.course_run_key) is None
|
||||
assert not _can_generate_regular_certificate(self.user, self.course_run_key, self.enrollment_mode,
|
||||
self.grade)
|
||||
assert _set_regular_cert_status(self.user, self.course_run_key, self.enrollment_mode, self.grade) is None
|
||||
|
||||
def test_can_generate_not_passing_no_cert(self):
|
||||
"""
|
||||
Test handling when the user does not have a passing grade and no cert exists
|
||||
"""
|
||||
with mock.patch(PASSING_GRADE_METHOD, return_value=False):
|
||||
assert not _can_generate_regular_certificate(self.user, self.course_run_key)
|
||||
assert _set_regular_cert_status(self.user, self.course_run_key) is None
|
||||
assert not _can_generate_regular_certificate(self.user, self.course_run_key, self.enrollment_mode,
|
||||
self.grade)
|
||||
assert _set_regular_cert_status(self.user, self.course_run_key, self.enrollment_mode, self.grade) is None
|
||||
|
||||
def test_can_generate_not_passing_cert(self):
|
||||
"""
|
||||
@@ -585,8 +614,9 @@ class CertificateTests(ModuleStoreTestCase):
|
||||
)
|
||||
|
||||
with mock.patch(PASSING_GRADE_METHOD, return_value=False):
|
||||
assert not _can_generate_regular_certificate(u, self.course_run_key)
|
||||
assert _set_regular_cert_status(u, self.course_run_key) == CertificateStatuses.notpassing
|
||||
assert not _can_generate_regular_certificate(u, self.course_run_key, self.enrollment_mode, self.grade)
|
||||
assert _set_regular_cert_status(u, self.course_run_key, self.enrollment_mode,
|
||||
self.grade) == CertificateStatuses.notpassing
|
||||
|
||||
def test_can_generate_not_enrolled(self):
|
||||
"""
|
||||
@@ -595,8 +625,10 @@ class CertificateTests(ModuleStoreTestCase):
|
||||
u = UserFactory()
|
||||
cr = CourseFactory()
|
||||
key = cr.id # pylint: disable=no-member
|
||||
assert not _can_generate_regular_certificate(u, key)
|
||||
assert _set_regular_cert_status(u, key) is None
|
||||
mode = None
|
||||
grade = None
|
||||
assert not _can_generate_regular_certificate(u, key, mode, grade)
|
||||
assert _set_regular_cert_status(u, key, mode, grade) is None
|
||||
|
||||
def test_can_generate_audit(self):
|
||||
"""
|
||||
@@ -605,15 +637,16 @@ class CertificateTests(ModuleStoreTestCase):
|
||||
u = UserFactory()
|
||||
cr = CourseFactory()
|
||||
key = cr.id # pylint: disable=no-member
|
||||
mode = CourseMode.AUDIT
|
||||
CourseEnrollmentFactory(
|
||||
user=u,
|
||||
course_id=key,
|
||||
is_active=True,
|
||||
mode=GeneratedCertificate.MODES.audit,
|
||||
mode=mode,
|
||||
)
|
||||
|
||||
assert not _can_generate_regular_certificate(u, key)
|
||||
assert _set_regular_cert_status(u, key) is None
|
||||
assert not _can_generate_regular_certificate(u, key, mode, self.grade)
|
||||
assert _set_regular_cert_status(u, key, mode, self.grade) is None
|
||||
|
||||
def test_can_generate_invalidated(self):
|
||||
"""
|
||||
@@ -640,24 +673,26 @@ class CertificateTests(ModuleStoreTestCase):
|
||||
active=True
|
||||
)
|
||||
|
||||
assert not _can_generate_regular_certificate(u, key)
|
||||
assert _set_regular_cert_status(u, key) == CertificateStatuses.unavailable
|
||||
assert not _can_generate_regular_certificate(u, key, self.enrollment_mode, self.grade)
|
||||
assert _set_regular_cert_status(u, key, self.enrollment_mode, self.grade) == CertificateStatuses.unavailable
|
||||
|
||||
def test_can_generate_web_cert_disabled(self):
|
||||
"""
|
||||
Test handling when web certs are not enabled
|
||||
"""
|
||||
with mock.patch(WEB_CERTS_METHOD, return_value=False):
|
||||
assert not _can_generate_regular_certificate(self.user, self.course_run_key)
|
||||
assert _set_regular_cert_status(self.user, self.course_run_key) is None
|
||||
assert not _can_generate_regular_certificate(self.user, self.course_run_key, self.enrollment_mode,
|
||||
self.grade)
|
||||
assert _set_regular_cert_status(self.user, self.course_run_key, self.enrollment_mode, self.grade) is None
|
||||
|
||||
def test_can_generate_no_overview(self):
|
||||
"""
|
||||
Test handling when the course overview is missing
|
||||
"""
|
||||
with mock.patch(COURSE_OVERVIEW_METHOD, return_value=None):
|
||||
assert not _can_generate_regular_certificate(self.user, self.course_run_key)
|
||||
assert _set_regular_cert_status(self.user, self.course_run_key) is None
|
||||
assert not _can_generate_regular_certificate(self.user, self.course_run_key, self.enrollment_mode,
|
||||
self.grade)
|
||||
assert _set_regular_cert_status(self.user, self.course_run_key, self.enrollment_mode, self.grade) is None
|
||||
|
||||
def test_cert_status_downloadable(self):
|
||||
"""
|
||||
@@ -679,4 +714,4 @@ class CertificateTests(ModuleStoreTestCase):
|
||||
status=CertificateStatuses.downloadable
|
||||
)
|
||||
|
||||
assert _set_regular_cert_status(u, key) is None
|
||||
assert _set_regular_cert_status(u, key, self.enrollment_mode, self.grade) is None
|
||||
|
||||
@@ -2082,7 +2082,7 @@ class TestCertificateGeneration(InstructorTaskModuleTestCase):
|
||||
'failed': 0,
|
||||
'skipped': 2
|
||||
}
|
||||
with self.assertNumQueries(66):
|
||||
with self.assertNumQueries(71):
|
||||
self.assertCertificatesGenerated(task_input, expected_results)
|
||||
|
||||
@ddt.data(
|
||||
|
||||
Reference in New Issue
Block a user