diff --git a/lms/djangoapps/certificates/api.py b/lms/djangoapps/certificates/api.py index d6c77f4e72..02b254ffad 100644 --- a/lms/djangoapps/certificates/api.py +++ b/lms/djangoapps/certificates/api.py @@ -299,6 +299,15 @@ def certificate_downloadable_status(student, course_key): response_data["earned_but_not_available"] = True response_data["certificate_available_date"] = course_overview.certificate_available_date + if ( + not certificates_viewable_for_course(course_overview) + and not CertificateStatuses.is_passing_status(current_status["status"]) + and display_behavior_is_valid + and course_overview.certificate_available_date + ): + response_data["not_earned_but_available_date"] = True + response_data["certificate_available_date"] = course_overview.certificate_available_date + may_view_certificate = _should_certificate_be_visible( course_overview.certificates_display_behavior, course_overview.certificates_show_before_end, diff --git a/lms/djangoapps/certificates/tests/test_api.py b/lms/djangoapps/certificates/tests/test_api.py index cdc9bc9326..ca12c98966 100644 --- a/lms/djangoapps/certificates/tests/test_api.py +++ b/lms/djangoapps/certificates/tests/test_api.py @@ -210,13 +210,14 @@ class CertificateDownloadableStatusTests(WebCertificateTestMixin, ModuleStoreTes } @ddt.data( - (True, timedelta(days=2), CertificatesDisplayBehaviors.END_WITH_DATE, True, None), - (False, -timedelta(days=2), CertificatesDisplayBehaviors.EARLY_NO_INFO, True, None), - (False, timedelta(days=2), CertificatesDisplayBehaviors.EARLY_NO_INFO, True, None), - (False, -timedelta(days=2), CertificatesDisplayBehaviors.END, True, None), - (False, timedelta(days=2), CertificatesDisplayBehaviors.END, False, True), - (False, -timedelta(days=2), CertificatesDisplayBehaviors.END_WITH_DATE, True, None), - (False, timedelta(days=2), CertificatesDisplayBehaviors.END_WITH_DATE, False, True), + (True, timedelta(days=2), CertificatesDisplayBehaviors.END_WITH_DATE, True, None, None), + (False, -timedelta(days=2), CertificatesDisplayBehaviors.EARLY_NO_INFO, True, None, None), + (False, timedelta(days=2), CertificatesDisplayBehaviors.EARLY_NO_INFO, True, None, None), + (False, -timedelta(days=2), CertificatesDisplayBehaviors.END, True, None, None), + (False, timedelta(days=2), CertificatesDisplayBehaviors.END, False, True, None), + (False, -timedelta(days=2), CertificatesDisplayBehaviors.END_WITH_DATE, True, None, None), + (False, timedelta(days=2), CertificatesDisplayBehaviors.END_WITH_DATE, False, True, None), + (False, timedelta(days=2), CertificatesDisplayBehaviors.END_WITH_DATE, False, None, True), ) @ddt.unpack @patch.dict(settings.FEATURES, {"CERTIFICATES_HTML_VIEW": True}) @@ -227,6 +228,7 @@ class CertificateDownloadableStatusTests(WebCertificateTestMixin, ModuleStoreTes certificates_display_behavior, cert_downloadable_status, earned_but_not_available, + no_earned_but_available_date, ): """ Test 'downloadable status' @@ -239,7 +241,14 @@ class CertificateDownloadableStatusTests(WebCertificateTestMixin, ModuleStoreTes self._setup_course_certificate() - downloadable_status = certificate_downloadable_status(self.student, self.course.id) + if no_earned_but_available_date: + downloadable_status = certificate_downloadable_status(self.student_no_cert, self.course.id) + + assert downloadable_status.get("not_earned_but_available_date") == no_earned_but_available_date + assert downloadable_status.get("certificate_available_date") is not None + else: + downloadable_status = certificate_downloadable_status(self.student, self.course.id) + assert downloadable_status["is_downloadable"] == cert_downloadable_status assert downloadable_status.get("earned_but_not_available") == earned_but_not_available diff --git a/lms/djangoapps/courseware/views/views.py b/lms/djangoapps/courseware/views/views.py index dc7a1dbf92..af157e76a0 100644 --- a/lms/djangoapps/courseware/views/views.py +++ b/lms/djangoapps/courseware/views/views.py @@ -170,6 +170,8 @@ CertData = namedtuple( ) EARNED_BUT_NOT_AVAILABLE_CERT_STATUS = 'earned_but_not_available' +NOT_EARNED_BUT_AVAILABLE_DATE_CERT_STATUS = 'not_earned_but_available_date' + AUDIT_PASSING_CERT_DATA = CertData( CertificateStatuses.audit_passing, _('Your enrollment: Audit track'), @@ -235,6 +237,17 @@ def _earned_but_not_available_cert_data(cert_downloadable_status): ) +def _not_earned_but_available_date_cert_data(cert_downloadable_status): + return CertData( + NOT_EARNED_BUT_AVAILABLE_DATE_CERT_STATUS, + _('Your certificate will be available after the indicated date'), + _('After this course officially ends, you will receive an email notification with your certificate.'), + download_url=None, + cert_web_view_url=None, + certificate_available_date=cert_downloadable_status.get('certificate_available_date') + ) + + def _downloadable_cert_data(download_url=None, cert_web_view_url=None): return CertData( CertificateStatuses.downloadable, @@ -1092,6 +1105,9 @@ def _certificate_message(student, course, enrollment_mode): # lint-amnesty, pyl if cert_downloadable_status.get('earned_but_not_available'): return _earned_but_not_available_cert_data(cert_downloadable_status) + if cert_downloadable_status.get('not_earned_but_available_date'): + return _not_earned_but_available_date_cert_data(cert_downloadable_status) + if cert_downloadable_status['is_generating']: return GENERATING_CERT_DATA @@ -1121,6 +1137,9 @@ def get_cert_data(student, course, enrollment_mode, course_grade=None): if cert_data.cert_status == EARNED_BUT_NOT_AVAILABLE_CERT_STATUS: return cert_data + if cert_data.cert_status == NOT_EARNED_BUT_AVAILABLE_DATE_CERT_STATUS: + return cert_data + certificates_enabled_for_course = certs_api.has_self_generated_certificates_enabled(course.id) if course_grade is None: course_grade = CourseGradeFactory().read(student, course)