From f99b89c8c5dfbfe74baa34b91513e75786ed33a3 Mon Sep 17 00:00:00 2001 From: Justin Hynes Date: Thu, 27 Mar 2025 08:41:38 -0400 Subject: [PATCH] feat: update date selection logic when rendering course certs (#36447) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: https://github.com/openedx/platform-roadmap/issues/423 This PR updates the logic for determining the issued date shown on course certificates. For courses with a display behavior set to 'end date of the course', certificates earned by learners will now show the course run’s end date as the issued date. --- lms/djangoapps/certificates/api.py | 15 ++++++-- lms/djangoapps/certificates/tests/test_api.py | 37 +++++++++++++++++++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/lms/djangoapps/certificates/api.py b/lms/djangoapps/certificates/api.py index 895822f588..fe3dce92a8 100644 --- a/lms/djangoapps/certificates/api.py +++ b/lms/djangoapps/certificates/api.py @@ -858,7 +858,14 @@ def available_date_for_certificate(course, certificate) -> datetime: def display_date_for_certificate(course, certificate): """ - Returns the display date that a certificate should display. + Returns the date that should be displayed on a certificate when rendered. + + If the certificate has a certificate date override associated with it, display the override date. + + Otherwise, if the course has a display behavior of "END_WITH_DATE", display the associated certificate available + date. If the course has a display behavior of "END", we should display the end date of the course. Lastly, when the + display behavior is "EARLY_NO_INFO" or when the course run is self-paced, we display the modified date of the + certificate instance. Arguments: course (CourseOverview or course block): The course we're getting the date for @@ -873,8 +880,10 @@ def display_date_for_certificate(course, certificate): if _course_uses_available_date(course) and course.certificate_available_date < datetime.now(UTC): return course.certificate_available_date - - return certificate.modified_date + elif course.certificates_display_behavior == CertificatesDisplayBehaviors.END and course.end: + return course.end + else: + return certificate.modified_date def is_valid_pdf_certificate(cert_data): diff --git a/lms/djangoapps/certificates/tests/test_api.py b/lms/djangoapps/certificates/tests/test_api.py index cb11b9e00b..11fc102fda 100644 --- a/lms/djangoapps/certificates/tests/test_api.py +++ b/lms/djangoapps/certificates/tests/test_api.py @@ -1135,6 +1135,43 @@ class CertificatesApiTestCase(TestCase): assert date == display_date_for_certificate(self.course, self.certificate) assert maybe_avail == available_date_for_certificate(self.course, self.certificate) + def test_display_date_for_certificate_cdb_early_no_info(self): + """ + Test to verify that the "earned date" displayed on a course certificate is the last modified date of a + certificate instance when the display behavior is set to EARLY_NO_INFO. + """ + with configure_waffle_namespace(True): + self.course.certificates_display_behavior = CertificatesDisplayBehaviors.EARLY_NO_INFO + assert display_date_for_certificate(self.course, self.certificate) == self.certificate.modified_date + + def test_display_date_for_certificate_cdb_end_with_date(self): + """ + Test to verify that the "earned date" displayed on a course certificate is the certificate available date + associated with the course when the display behavior is set to END_WITH_DATE. + """ + with configure_waffle_namespace(True): + self.course.certificates_display_behavior = CertificatesDisplayBehaviors.END_WITH_DATE + self.course.certificate_available_date = datetime(2017, 2, 1, tzinfo=pytz.UTC) + assert display_date_for_certificate(self.course, self.certificate) == self.course.certificate_available_date + + def test_display_date_for_certificate_cdb_end(self): + """ + Test to verify that the "earned date" displayed on a course certificate is the end date of the course run + when the display behavior is set to END. + """ + with configure_waffle_namespace(True): + self.course.certificates_display_behavior = CertificatesDisplayBehaviors.END + assert display_date_for_certificate(self.course, self.certificate) == self.course.end + + def test_display_date_for_certificate_date_override(self): + """ + Test to verify that the "earned date" displayed on a course certificate is the certificate override date + if-and-only-if date override associated with the certificate instance. + """ + with configure_waffle_namespace(True): + self.certificate.date_override = datetime(2016, 1, 1, tzinfo=pytz.UTC) + assert display_date_for_certificate(self.course, self.certificate) == self.certificate.date_override.date + @ddt.ddt class CertificatesMessagingTestCase(ModuleStoreTestCase):