diff --git a/lms/djangoapps/certificates/management/commands/tests/test_cert_management.py b/lms/djangoapps/certificates/management/commands/tests/test_cert_management.py index 864e846c78..381517aa22 100644 --- a/lms/djangoapps/certificates/management/commands/tests/test_cert_management.py +++ b/lms/djangoapps/certificates/management/commands/tests/test_cert_management.py @@ -141,7 +141,7 @@ class ResubmitErrorCertificatesTest(CertificateManagementTest): def test_course_does_not_exist(self): phantom_course = CourseLocator(org='phantom', course='phantom', run='phantom') - self._create_cert(phantom_course, self.user, 'error') + self._create_cert(phantom_course, self.user, CertificateStatuses.error) call_command(self.command) # Expect that the certificate was NOT resubmitted diff --git a/lms/djangoapps/certificates/models.py b/lms/djangoapps/certificates/models.py index 4555bbd6a9..0b49df33ce 100644 --- a/lms/djangoapps/certificates/models.py +++ b/lms/djangoapps/certificates/models.py @@ -39,7 +39,26 @@ User = get_user_model() class CertificateStatuses: """ - Enum for certificate statuses + Enum for certificate statuses. + + Not all of these status are currently used. Some are kept for historical reasons and because existing course + certificates may have been granted that status. + + audit_notpassing - User is in the audit track and has not achieved a passing grade. + audit_passing - User is in the audit track and has achieved a passing grade. + deleted - The PDF certificate has been deleted. + deleting - A request has been made to delete the PDF certificate. + downloadable - The user has been granted this certificate and the certificate is ready and available. + error - An error occurred during PDF certificate generation. + generating - A request has been made to generate a PDF certificate, but it has not been generated yet. + honor_passing - User is in the honor track and has achieved a passing grade. + invalidated - Certificate is not valid. + notpassing - The user has not achieved a passing grade. + requesting - A request has been made to generate the PDF certificate. + restricted - The user is on the restricted list. This status was previously set if allow_certificate was + set to False in the userprofile table. + unavailable - Certificate has been invalidated. + unverified - The user is in verified track but does not have an approved, unexpired identity verification. """ deleted = 'deleted' deleting = 'deleting' @@ -200,6 +219,10 @@ class GeneratedCertificate(models.Model): .. pii: PII can exist in the generated certificate linked to in this model. Certificate data is currently retained. .. pii_types: name, username .. pii_retirement: retained + + The grade stored in this model is set at the same time as the status. This GeneratedCertificate grade is *not* + updated whenever the user's course grade changes and so it should not be considered the source of truth. It is + suggested that the PersistentCourseGrade be used instead of the GeneratedCertificate grade. """ # Import here instead of top of file since this module gets imported before # the course_modes app is loaded, resulting in a Django deprecation warning. @@ -570,24 +593,6 @@ def certificate_status_for_student(student, course_id): def certificate_status(generated_certificate): """ This returns a dictionary with a key for status, and other information. - The status is one of the following: - - unavailable - No entry for this student--if they are actually in - the course, they probably have not been graded for - certificate generation yet. - generating - A request has been made to generate a certificate, - but it has not been generated yet. - deleting - A request has been made to delete a certificate. - - deleted - The certificate has been deleted. - downloadable - The certificate is available for download. - notpassing - The student was graded but is not passing - restricted - The student is on the restricted list. This status was - previously set if allow_certificate was set to False in - the userprofile table. - unverified - The student is in verified enrollment track and - the student did not have their identity verified, - even though they should be eligible for the cert otherwise. If the status is "downloadable", the dictionary also contains "download_url". diff --git a/lms/djangoapps/certificates/signals.py b/lms/djangoapps/certificates/signals.py index 19cfabcbc9..5ff52a8233 100644 --- a/lms/djangoapps/certificates/signals.py +++ b/lms/djangoapps/certificates/signals.py @@ -200,7 +200,8 @@ def _fire_ungenerated_certificate_task(user, course_key, expected_verification_s cert = GeneratedCertificate.certificate_for_student(user, course_key) generate_learner_certificate = ( - enrollment_mode in allowed_enrollment_modes_list and (cert is None or cert.status == 'unverified') + enrollment_mode in allowed_enrollment_modes_list and ( + cert is None or cert.status == CertificateStatuses.unverified) ) if generate_learner_certificate: diff --git a/lms/djangoapps/certificates/tests/test_api.py b/lms/djangoapps/certificates/tests/test_api.py index 7046688778..233498cf14 100644 --- a/lms/djangoapps/certificates/tests/test_api.py +++ b/lms/djangoapps/certificates/tests/test_api.py @@ -573,7 +573,7 @@ class GenerateUserCertificatesTest(EventTestMixin, WebCertificateTestMixin, Modu # Verify that the certificate has been marked with status error cert = GeneratedCertificate.eligible_certificates.get(user=self.student, course_id=self.course.id) - assert cert.status == 'error' + assert cert.status == CertificateStatuses.error assert self.ERROR_REASON in cert.error_reason def test_generate_user_certificates_with_unverified_cert_status(self): @@ -594,7 +594,7 @@ class GenerateUserCertificatesTest(EventTestMixin, WebCertificateTestMixin, Modu with mock_passing_grade(): with self._mock_queue(): status = generate_user_certificates(self.student, self.course.id) - assert status == 'generating' + assert status == CertificateStatuses.generating @patch.dict(settings.FEATURES, {'CERTIFICATES_HTML_VIEW': True}) def test_new_cert_requests_returns_generating_for_html_certificate(self): diff --git a/lms/djangoapps/certificates/tests/test_views.py b/lms/djangoapps/certificates/tests/test_views.py index e6aab9fb86..7d862cb2f0 100644 --- a/lms/djangoapps/certificates/tests/test_views.py +++ b/lms/djangoapps/certificates/tests/test_views.py @@ -16,6 +16,7 @@ from opaque_keys.edx.locator import CourseLocator from common.djangoapps.student.tests.factories import UserFactory from lms.djangoapps.certificates.models import ( CertificateHtmlViewConfiguration, + CertificateStatuses, ExampleCertificate, ExampleCertificateSet ) @@ -231,7 +232,7 @@ class CertificatesViewsSiteTests(ModuleStoreTestCase): grade="0.95", key='the_key', distinction=True, - status='downloadable', + status=CertificateStatuses.downloadable, mode='honor', name=self.user.profile.name, verify_uuid=uuid4().hex diff --git a/lms/djangoapps/certificates/tests/test_webview_views.py b/lms/djangoapps/certificates/tests/test_webview_views.py index f2896ccb7b..89f88643ca 100644 --- a/lms/djangoapps/certificates/tests/test_webview_views.py +++ b/lms/djangoapps/certificates/tests/test_webview_views.py @@ -103,7 +103,7 @@ class CommonCertificatesTestCase(ModuleStoreTestCase): grade="0.95", key='the_key', distinction=True, - status='downloadable', + status=CertificateStatuses.downloadable, mode='honor', name=self.user.profile.name, )