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:
oliviaruizknott
2021-08-11 15:13:13 -06:00
parent 7d029f8283
commit ecba21dcc0
4 changed files with 101 additions and 2 deletions

View File

@@ -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

View File

@@ -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"

View File

@@ -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):

View File

@@ -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