feat: Display date override on certificate
If the certificate has an associated certificate date override, display that date on the certificate instead of any other date. The date override should not affect whether or not the certificate is visible / available; only the date displayed on the certificate.
This commit is contained in:
@@ -852,8 +852,15 @@ def display_date_for_certificate(course, certificate):
|
||||
Returns:
|
||||
datetime.date
|
||||
"""
|
||||
try:
|
||||
if certificate.date_override:
|
||||
return certificate.date_override.date
|
||||
except ObjectDoesNotExist:
|
||||
pass
|
||||
|
||||
if _course_uses_available_date(course) and course.certificate_available_date < datetime.now(UTC):
|
||||
return course.certificate_available_date
|
||||
|
||||
return certificate.modified_date
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ Certificates factories
|
||||
"""
|
||||
|
||||
|
||||
import datetime
|
||||
from uuid import uuid4
|
||||
|
||||
from factory.django import DjangoModelFactory
|
||||
@@ -10,6 +11,7 @@ from factory.django import DjangoModelFactory
|
||||
from common.djangoapps.student.models import LinkedInAddToProfileConfiguration
|
||||
from lms.djangoapps.certificates.models import (
|
||||
CertificateAllowlist,
|
||||
CertificateDateOverride,
|
||||
CertificateHtmlViewConfiguration,
|
||||
CertificateInvalidation,
|
||||
CertificateStatuses,
|
||||
@@ -104,3 +106,14 @@ class LinkedInAddToProfileConfigurationFactory(DjangoModelFactory):
|
||||
|
||||
enabled = True
|
||||
company_identifier = "1337"
|
||||
|
||||
|
||||
class CertificateDateOverrideFactory(DjangoModelFactory):
|
||||
"""
|
||||
CertificateDateOverride factory
|
||||
"""
|
||||
class Meta:
|
||||
model = CertificateDateOverride
|
||||
|
||||
date = datetime.datetime(2021, 5, 11)
|
||||
reason = "Learner really wanted this on their birthday"
|
||||
|
||||
@@ -1131,6 +1131,7 @@ class MockGeneratedCertificate:
|
||||
self.status = status
|
||||
self.created_date = datetime.now(pytz.UTC)
|
||||
self.modified_date = datetime.now(pytz.UTC)
|
||||
self.date_override = None
|
||||
|
||||
def is_valid(self):
|
||||
"""
|
||||
@@ -1139,6 +1140,11 @@ class MockGeneratedCertificate:
|
||||
return self.status == CertificateStatuses.downloadable
|
||||
|
||||
|
||||
class MockCertificateDateOverride:
|
||||
def __init__(self, date=None):
|
||||
self.date = date or datetime.now(pytz.UTC)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def configure_waffle_namespace(feature_enabled):
|
||||
"""
|
||||
@@ -1221,6 +1227,12 @@ class CertificatesApiTestCase(TestCase):
|
||||
assert maybe_avail == available_date_for_certificate(self.course, self.certificate)
|
||||
assert self.certificate.modified_date == display_date_for_certificate(self.course, self.certificate)
|
||||
|
||||
# With a certificate date override, display date returns the override, available date ignores it
|
||||
self.certificate.date_override = MockCertificateDateOverride()
|
||||
date = self.certificate.date_override.date
|
||||
assert date == display_date_for_certificate(self.course, self.certificate)
|
||||
assert maybe_avail == available_date_for_certificate(self.course, self.certificate)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class CertificatesMessagingTestCase(ModuleStoreTestCase):
|
||||
|
||||
@@ -38,6 +38,7 @@ from lms.djangoapps.certificates.models import (
|
||||
GeneratedCertificate
|
||||
)
|
||||
from lms.djangoapps.certificates.tests.factories import (
|
||||
CertificateDateOverrideFactory,
|
||||
CertificateHtmlViewConfigurationFactory,
|
||||
GeneratedCertificateFactory,
|
||||
LinkedInAddToProfileConfigurationFactory
|
||||
@@ -237,6 +238,15 @@ class CommonCertificatesTestCase(ModuleStoreTestCase):
|
||||
)
|
||||
template.save()
|
||||
|
||||
def _add_certificate_date_override(self):
|
||||
"""
|
||||
Creates a mock CertificateDateOverride and adds it to the certificate
|
||||
"""
|
||||
self.cert.date_override = CertificateDateOverrideFactory.create(
|
||||
generated_certificate=self.cert,
|
||||
overridden_by=self.user,
|
||||
)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase):
|
||||
@@ -646,10 +656,13 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
|
||||
response = self.client.get(test_url)
|
||||
self.assertContains(response, '<html class="no-js" lang="ar">')
|
||||
|
||||
@ddt.data(False, True)
|
||||
@override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED)
|
||||
def test_html_view_for_non_viewable_certificate_and_for_student_user(self):
|
||||
def test_html_view_for_non_viewable_certificate_and_for_student_user(self, date_override):
|
||||
"""
|
||||
Tests that Certificate HTML Web View returns "Cannot Find Certificate" if certificate is not viewable yet.
|
||||
Tests that Certificate HTML Web View returns "Cannot Find Certificate"
|
||||
if certificate is not viewable yet, regardless of certificate date
|
||||
override
|
||||
"""
|
||||
test_certificates = [
|
||||
{
|
||||
@@ -660,6 +673,12 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
|
||||
'is_active': True
|
||||
}
|
||||
]
|
||||
|
||||
# A certificate with an available date in the future should not be
|
||||
# viewable, regardless of the date override.
|
||||
if date_override:
|
||||
self._add_certificate_date_override()
|
||||
|
||||
self.course.certificates = {'certificates': test_certificates}
|
||||
self.course.cert_html_view_enabled = True
|
||||
self.course.certificate_available_date = datetime.datetime.today() + datetime.timedelta(days=1)
|
||||
@@ -945,6 +964,54 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
|
||||
)
|
||||
self.assertContains(response, date)
|
||||
|
||||
@override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED)
|
||||
@ddt.data(
|
||||
(True, False),
|
||||
(False, False),
|
||||
(True, True),
|
||||
(False, True)
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_html_view_certificate_display_date(self, self_paced, date_override):
|
||||
"""
|
||||
Test certificate web view should display the correct date on the
|
||||
certificate in all cases:
|
||||
* self-paced, no date override
|
||||
* instructor-paced with certificate_available_date
|
||||
* self-paced with date override
|
||||
* instructor-paced with date override
|
||||
"""
|
||||
self.course.self_paced = self_paced
|
||||
if date_override:
|
||||
self._add_certificate_date_override()
|
||||
today = datetime.datetime.utcnow()
|
||||
self.course.certificate_available_date = today + datetime.timedelta(-2)
|
||||
self.store.update_item(self.course, self.user.id)
|
||||
self._add_course_certificates(count=1, signatory_count=1, is_active=True)
|
||||
test_url = get_certificate_url(
|
||||
user_id=self.user.id,
|
||||
course_id=str(self.course.id),
|
||||
uuid=self.cert.verify_uuid
|
||||
)
|
||||
|
||||
with override_waffle_switch(AUTO_CERTIFICATE_GENERATION, active=True):
|
||||
response = self.client.get(test_url)
|
||||
|
||||
if date_override:
|
||||
expected_date = self.cert.date_override.date
|
||||
elif self_paced or self.course.certificate_available_date > today:
|
||||
expected_date = today
|
||||
else:
|
||||
expected_date = self.course.certificate_available_date
|
||||
|
||||
date = '{month} {day}, {year}'.format(
|
||||
month=strftime_localized(expected_date, "%B"),
|
||||
day=expected_date.day,
|
||||
year=expected_date.year
|
||||
)
|
||||
|
||||
self.assertContains(response, date)
|
||||
|
||||
@override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED)
|
||||
def test_render_html_view_invalid_certificate_configuration(self):
|
||||
self.course.cert_html_view_enabled = True
|
||||
|
||||
Reference in New Issue
Block a user