<%
- certificate_available_date_string = course_overview.certificate_available_date.strftime('%Y-%m-%dT%H:%M:%S%z')
+ if settings.FEATURES.get("ENABLE_V2_CERT_DISPLAY_SETTINGS", False):
+ certificate_available_date_string = ""
+ if course_overview.certificates_display_behavior == CertificatesDisplayBehaviors.END_WITH_DATE and course_overview.certificate_available_date:
+ certificate_available_date_string = course_overview.certificate_available_date.strftime('%Y-%m-%dT%H:%M:%S%z')
+ elif course_overview.certificates_display_behavior == CertificatesDisplayBehaviors.END and course_overview.end:
+ certificate_available_date_string = course_overview.end.strftime('%Y-%m-%dT%H:%M:%S%z')
+ else:
+ certificate_available_date_string = course_overview.certificate_available_date.strftime('%Y-%m-%dT%H:%M:%S%z')
container_string = _("Your grade and certificate will be ready after {date}.")
format = 'shortDate'
%>
diff --git a/openedx/core/djangoapps/certificates/api.py b/openedx/core/djangoapps/certificates/api.py
index 68f7a326d8..5e12095a63 100644
--- a/openedx/core/djangoapps/certificates/api.py
+++ b/openedx/core/djangoapps/certificates/api.py
@@ -7,10 +7,13 @@ import logging
from datetime import datetime
from pytz import UTC
+from django.conf import settings
+
from lms.djangoapps.certificates import api as certs_api
from lms.djangoapps.certificates.data import CertificateStatuses
from openedx.core.djangoapps.certificates.config import waffle
from common.djangoapps.student.models import CourseEnrollment
+from xmodule.data import CertificatesDisplayBehaviors
log = logging.getLogger(__name__)
@@ -56,7 +59,17 @@ def can_show_certificate_available_date_field(course):
def _course_uses_available_date(course):
- return can_show_certificate_available_date_field(course) and course.certificate_available_date
+ """Returns if the course has an certificate_available_date set and that it should be used"""
+ if settings.FEATURES.get("ENABLE_V2_CERT_DISPLAY_SETTINGS"):
+ display_behavior_is_valid = course.certificates_display_behavior == CertificatesDisplayBehaviors.END_WITH_DATE
+ else:
+ display_behavior_is_valid = True
+
+ return (
+ can_show_certificate_available_date_field(course)
+ and course.certificate_available_date
+ and display_behavior_is_valid
+ )
def available_date_for_certificate(course, certificate, certificate_available_date=None):
@@ -64,7 +77,7 @@ def available_date_for_certificate(course, certificate, certificate_available_da
Returns the available date to use with a certificate
Arguments:
- course (CourseOverview): The course we're checking
+ course (CourseOverview or course descriptor): The course we're checking
certificate (GeneratedCertificate): The certificate we're getting the date for
certificate_available_date (datetime): An optional date to override the from the course overview.
"""
@@ -74,6 +87,14 @@ def available_date_for_certificate(course, certificate, certificate_available_da
def display_date_for_certificate(course, certificate):
+ """
+ Returns the display date that a certificate should display.
+
+ Arguments:
+ course (CourseOverview or course descriptor): The course we're getting the date for
+ Returns:
+ datetime.date
+ """
if _course_uses_available_date(course) and course.certificate_available_date < datetime.now(UTC):
return course.certificate_available_date
return certificate.modified_date
diff --git a/openedx/core/djangoapps/certificates/tests/test_api.py b/openedx/core/djangoapps/certificates/tests/test_api.py
index 1e7e933146..15c607391e 100644
--- a/openedx/core/djangoapps/certificates/tests/test_api.py
+++ b/openedx/core/djangoapps/certificates/tests/test_api.py
@@ -11,10 +11,11 @@ from edx_toggles.toggles import LegacyWaffleSwitch
from edx_toggles.toggles.testutils import override_waffle_switch
from common.djangoapps.course_modes.models import CourseMode
+from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
from openedx.core.djangoapps.certificates import api
from openedx.core.djangoapps.certificates.config import waffle as certs_waffle
from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory
-from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
+from xmodule.data import CertificatesDisplayBehaviors
# TODO: Copied from lms.djangoapps.certificates.models,
@@ -167,6 +168,7 @@ class CertificatesApiTestCase(TestCase):
# With an available date set in the past, both return the available date (if configured)
self.course.certificate_available_date = datetime(2017, 2, 1, tzinfo=pytz.UTC)
+ self.course.certificates_display_behavior = CertificatesDisplayBehaviors.END_WITH_DATE
maybe_avail = self.course.certificate_available_date if uses_avail_date else self.certificate.modified_date
assert maybe_avail == api.available_date_for_certificate(self.course, self.certificate)
assert maybe_avail == api.display_date_for_certificate(self.course, self.certificate)
diff --git a/openedx/core/djangoapps/content/course_overviews/models.py b/openedx/core/djangoapps/content/course_overviews/models.py
index 50ed256a3a..46e839052a 100644
--- a/openedx/core/djangoapps/content/course_overviews/models.py
+++ b/openedx/core/djangoapps/content/course_overviews/models.py
@@ -36,7 +36,6 @@ from xmodule.error_module import ErrorBlock
from xmodule.modulestore.django import modulestore
from xmodule.tabs import CourseTab
-
log = logging.getLogger(__name__)
@@ -55,6 +54,10 @@ class CourseOverview(TimeStampedModel):
course catalog (courses to enroll in)
course about (meta data about the course)
+ When you bump the VERSION you will invalidate all existing course overviews. This
+ will cause a slew of modulestore reads as each course needs to be re-cached into
+ the course overview.
+
.. no_pii:
"""
@@ -62,7 +65,7 @@ class CourseOverview(TimeStampedModel):
app_label = 'course_overviews'
# IMPORTANT: Bump this whenever you modify this model and/or add a migration.
- VERSION = 15
+ VERSION = 16
# Cache entry versioning.
version = IntegerField()
@@ -210,13 +213,18 @@ class CourseOverview(TimeStampedModel):
course_overview.course_image_url = course_image_url(course)
course_overview.social_sharing_url = course.social_sharing_url
- course_overview.certificates_display_behavior = course.certificates_display_behavior
+ updated_available_date, updated_display_behavior = CourseDetails.validate_certificate_settings(
+ course.certificate_available_date,
+ course.certificates_display_behavior
+ )
+
+ course_overview.certificates_display_behavior = updated_display_behavior
+ course_overview.certificate_available_date = updated_available_date
course_overview.certificates_show_before_end = course.certificates_show_before_end
course_overview.cert_html_view_enabled = course.cert_html_view_enabled
course_overview.has_any_active_web_certificate = (get_active_web_certificate(course) is not None)
course_overview.cert_name_short = course.cert_name_short
course_overview.cert_name_long = course.cert_name_long
- course_overview.certificate_available_date = course.certificate_available_date
course_overview.lowest_passing_grade = lowest_passing_grade
course_overview.end_of_course_survey_url = course.end_of_course_survey_url
diff --git a/openedx/core/djangoapps/content/course_overviews/tests/test_signals.py b/openedx/core/djangoapps/content/course_overviews/tests/test_signals.py
index aea25829fd..66f5d39b8c 100644
--- a/openedx/core/djangoapps/content/course_overviews/tests/test_signals.py
+++ b/openedx/core/djangoapps/content/course_overviews/tests/test_signals.py
@@ -2,16 +2,21 @@
import datetime
from unittest.mock import patch
+from collections import namedtuple
import pytest
import ddt
+from xmodule.data import CertificatesDisplayBehaviors
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory, check_mongo_calls
from ..models import CourseOverview
+# represents a change of a course overview field. Used to avoid confusing indicies
+Change = namedtuple("Change", ["field_name", "initial_value", "changed_value"])
+
@ddt.ddt
class CourseOverviewSignalsTestCase(ModuleStoreTestCase):
@@ -71,8 +76,11 @@ class CourseOverviewSignalsTestCase(ModuleStoreTestCase):
self.store.delete_course(course.id, ModuleStoreEnum.UserID.test)
CourseOverview.get_from_id(course.id)
- def assert_changed_signal_sent(self, field_name, initial_value, changed_value, mock_signal): # lint-amnesty, pylint: disable=missing-function-docstring
- course = CourseFactory.create(emit_signals=True, **{field_name: initial_value})
+ def assert_changed_signal_sent(self, changes, mock_signal): # lint-amnesty, pylint: disable=missing-function-docstring
+ course = CourseFactory.create(
+ emit_signals=True,
+ **{change.field_name: change.initial_value for change in changes}
+ )
# changing display name doesn't fire the signal
course.display_name = course.display_name + 'changed'
@@ -80,18 +88,27 @@ class CourseOverviewSignalsTestCase(ModuleStoreTestCase):
assert not mock_signal.called
# changing the given field fires the signal
- setattr(course, field_name, changed_value)
+ for change in changes:
+ setattr(course, change.field_name, change.changed_value)
self.store.update_item(course, ModuleStoreEnum.UserID.test)
assert mock_signal.called
@patch('openedx.core.djangoapps.content.course_overviews.signals.COURSE_START_DATE_CHANGED.send')
def test_start_changed(self, mock_signal):
- self.assert_changed_signal_sent('start', self.TODAY, self.NEXT_WEEK, mock_signal)
+ self.assert_changed_signal_sent([Change('start', self.TODAY, self.NEXT_WEEK)], mock_signal)
@patch('openedx.core.djangoapps.content.course_overviews.signals.COURSE_PACING_CHANGED.send')
def test_pacing_changed(self, mock_signal):
- self.assert_changed_signal_sent('self_paced', True, False, mock_signal)
+ self.assert_changed_signal_sent([Change('self_paced', True, False)], mock_signal)
@patch('openedx.core.djangoapps.content.course_overviews.signals.COURSE_CERT_DATE_CHANGE.send_robust')
def test_cert_date_changed(self, mock_signal):
- self.assert_changed_signal_sent('certificate_available_date', self.TODAY, self.NEXT_WEEK, mock_signal)
+ changes = [
+ Change("certificate_available_date", self.TODAY, self.NEXT_WEEK),
+ Change(
+ "certificates_display_behavior",
+ CertificatesDisplayBehaviors.END,
+ CertificatesDisplayBehaviors.END_WITH_DATE
+ )
+ ]
+ self.assert_changed_signal_sent(changes, mock_signal)
diff --git a/openedx/core/djangoapps/courseware_api/tests/test_views.py b/openedx/core/djangoapps/courseware_api/tests/test_views.py
index 8625aa8d8f..64d2710653 100644
--- a/openedx/core/djangoapps/courseware_api/tests/test_views.py
+++ b/openedx/core/djangoapps/courseware_api/tests/test_views.py
@@ -2,7 +2,7 @@
Tests for courseware API
"""
import unittest
-from datetime import datetime
+from datetime import datetime, timedelta
from urllib.parse import urlencode
from typing import Optional
@@ -39,6 +39,7 @@ from common.djangoapps.student.roles import CourseInstructorRole
from common.djangoapps.student.tests.factories import CourseEnrollmentCelebrationFactory, UserFactory
from openedx.core.djangoapps.agreements.api import create_integrity_signature
from openedx.core.djangoapps.agreements.toggles import ENABLE_INTEGRITY_SIGNATURE
+from xmodule.data import CertificatesDisplayBehaviors
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE, SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import ItemFactory, ToyCourseFactory
@@ -46,6 +47,8 @@ from xmodule.modulestore.tests.factories import ItemFactory, ToyCourseFactory
User = get_user_model()
+_NEXT_WEEK = datetime.now() + timedelta(days=7)
+
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
class BaseCoursewareTests(SharedModuleStoreTestCase):
@@ -64,6 +67,8 @@ class BaseCoursewareTests(SharedModuleStoreTestCase):
enrollment_end=datetime(2028, 1, 1, 1, 1, 1),
emit_signals=True,
modulestore=cls.store,
+ certificate_available_date=_NEXT_WEEK,
+ certificates_display_behavior=CertificatesDisplayBehaviors.END_WITH_DATE
)
cls.chapter = ItemFactory(parent=cls.course, category='chapter')
cls.sequence = ItemFactory(parent=cls.chapter, category='sequential', display_name='sequence')
diff --git a/openedx/core/djangoapps/models/course_details.py b/openedx/core/djangoapps/models/course_details.py
index 748e90b591..b27522d47e 100644
--- a/openedx/core/djangoapps/models/course_details.py
+++ b/openedx/core/djangoapps/models/course_details.py
@@ -10,6 +10,7 @@ from django.conf import settings
from openedx.core.djangolib.markup import HTML
from openedx.core.lib.courses import course_image_url
+from xmodule.data import CertificatesDisplayBehaviors
from xmodule.fields import Date
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import ItemNotFoundError
@@ -48,6 +49,8 @@ class CourseDetails:
self.end_date = None # 'end'
self.enrollment_start = None
self.enrollment_end = None
+ self.certificate_available_date = None
+ self.certificates_display_behavior = None
self.syllabus = None # a pdf file asset
self.title = ""
self.subtitle = ""
@@ -108,7 +111,12 @@ class CourseDetails:
course_details = cls(course_key.org, course_key.course, course_key.run)
course_details.start_date = course_descriptor.start
course_details.end_date = course_descriptor.end
- course_details.certificate_available_date = course_descriptor.certificate_available_date # lint-amnesty, pylint: disable=attribute-defined-outside-init
+ updated_available_date, updated_display_behavior = cls.validate_certificate_settings(
+ course_descriptor.certificate_available_date,
+ course_descriptor.certificates_display_behavior
+ )
+ course_details.certificate_available_date = updated_available_date
+ course_details.certificates_display_behavior = updated_display_behavior
course_details.enrollment_start = course_descriptor.enrollment_start
course_details.enrollment_end = course_descriptor.enrollment_end
course_details.pre_requisite_courses = course_descriptor.pre_requisite_courses
@@ -244,6 +252,13 @@ class CourseDetails:
dirty = True
descriptor.certificate_available_date = converted
+ if (
+ 'certificates_display_behavior' in jsondict
+ and jsondict['certificates_display_behavior'] != descriptor.certificates_display_behavior
+ ):
+ descriptor.certificates_display_behavior = jsondict['certificates_display_behavior']
+ dirty = True
+
if 'course_image_name' in jsondict and jsondict['course_image_name'] != descriptor.course_image:
descriptor.course_image = jsondict['course_image_name']
dirty = True
@@ -338,3 +353,31 @@ class CourseDetails:
'frameborder="0" allowfullscreen="">').format(video_key)
)
return result
+
+ @classmethod
+ def validate_certificate_settings(cls, certificate_available_date, certificates_display_behavior):
+ """
+ Takes the stored values for certificate_available_date and certificates_display_behavior and verifies they work
+ together in tandem per ADR: lms/djangoapps/certificates/docs/decisions/005-cert-display-settings.rst
+
+ Arguments:
+ stored_certificate_available_date (str): certificate_available_date from the modulestore
+ stored_certificates_display_behavior (str):
+
+ Returns:
+ tuple[str, str]: updated certificate_available_date, updated certificates_display_behavior
+ None
+ """
+ # If V2 is not enable, return original values
+ if not settings.FEATURES.get("ENABLE_V2_CERT_DISPLAY_SETTINGS", False):
+ return (certificate_available_date, certificates_display_behavior)
+
+ # "early_no_info" will always show regardless of settings
+ if certificates_display_behavior == CertificatesDisplayBehaviors.EARLY_NO_INFO:
+ return (None, CertificatesDisplayBehaviors.EARLY_NO_INFO)
+
+ # If the date is set and "early_no_info" isn't
+ if certificate_available_date:
+ return (certificate_available_date, CertificatesDisplayBehaviors.END_WITH_DATE)
+
+ return (None, CertificatesDisplayBehaviors.END)
diff --git a/openedx/core/djangoapps/models/tests/test_course_details.py b/openedx/core/djangoapps/models/tests/test_course_details.py
index fcd3f27ed5..c7593ba686 100644
--- a/openedx/core/djangoapps/models/tests/test_course_details.py
+++ b/openedx/core/djangoapps/models/tests/test_course_details.py
@@ -7,12 +7,18 @@ import datetime
import pytest
import ddt
from pytz import UTC
+from unittest.mock import patch
+
+from django.conf import settings
from openedx.core.djangoapps.models.course_details import ABOUT_ATTRIBUTES, CourseDetails
from xmodule.modulestore import ModuleStoreEnum
+from xmodule.data import CertificatesDisplayBehaviors
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
+EXAMPLE_CERTIFICATE_AVAILABLE_DATE = datetime.date(2020, 1, 1)
+
@ddt.ddt
class CourseDetailsTestCase(ModuleStoreTestCase):
@@ -142,3 +148,82 @@ class CourseDetailsTestCase(ModuleStoreTestCase):
assert CourseDetails.fetch_youtube_video_id(self.course.id) == video_value
video_url = CourseDetails.fetch_video_url(self.course.id)
self.assertRegex(video_url, fr'http://.*{video_value}')
+
+ @ddt.data(
+ (
+ EXAMPLE_CERTIFICATE_AVAILABLE_DATE,
+ CertificatesDisplayBehaviors.END,
+ EXAMPLE_CERTIFICATE_AVAILABLE_DATE,
+ CertificatesDisplayBehaviors.END_WITH_DATE
+ ),
+ (
+ EXAMPLE_CERTIFICATE_AVAILABLE_DATE,
+ CertificatesDisplayBehaviors.END_WITH_DATE,
+ EXAMPLE_CERTIFICATE_AVAILABLE_DATE,
+ CertificatesDisplayBehaviors.END_WITH_DATE
+ ),
+ (
+ EXAMPLE_CERTIFICATE_AVAILABLE_DATE,
+ CertificatesDisplayBehaviors.EARLY_NO_INFO,
+ None,
+ CertificatesDisplayBehaviors.EARLY_NO_INFO
+ ),
+ (
+ EXAMPLE_CERTIFICATE_AVAILABLE_DATE,
+ "invalid_option",
+ EXAMPLE_CERTIFICATE_AVAILABLE_DATE,
+ CertificatesDisplayBehaviors.END_WITH_DATE
+ ),
+ (
+ None,
+ CertificatesDisplayBehaviors.END,
+ None,
+ CertificatesDisplayBehaviors.END
+ ),
+ (
+ None,
+ CertificatesDisplayBehaviors.END_WITH_DATE,
+ None,
+ CertificatesDisplayBehaviors.END
+ ),
+ (
+ None,
+ CertificatesDisplayBehaviors.EARLY_NO_INFO,
+ None,
+ CertificatesDisplayBehaviors.EARLY_NO_INFO
+ ),
+ (
+ None,
+ "invalid_option",
+ None,
+ CertificatesDisplayBehaviors.END
+ ),
+ )
+ @ddt.unpack
+ @patch.dict(settings.FEATURES, ENABLE_V2_CERT_DISPLAY_SETTINGS=True)
+ def test_validate_certificate_settings_v2(self, stored_date, stored_behavior, expected_date, expected_behavior):
+ assert CourseDetails.validate_certificate_settings(
+ stored_date, stored_behavior
+ ) == (expected_date, expected_behavior)
+
+ @ddt.data(
+ (
+ EXAMPLE_CERTIFICATE_AVAILABLE_DATE,
+ CertificatesDisplayBehaviors.END_WITH_DATE,
+ EXAMPLE_CERTIFICATE_AVAILABLE_DATE,
+ CertificatesDisplayBehaviors.END_WITH_DATE
+ ),
+ (
+ None,
+ "invalid_option",
+ None,
+ "invalid_option"
+ ),
+ )
+ @ddt.unpack
+ @patch.dict(settings.FEATURES, ENABLE_V2_CERT_DISPLAY_SETTINGS=False)
+ def test_validate_certificate_settings_v1(self, stored_date, stored_behavior, expected_date, expected_behavior):
+ """Test that method just returns passed in arguments if v2 is off"""
+ assert CourseDetails.validate_certificate_settings(
+ stored_date, stored_behavior
+ ) == (expected_date, expected_behavior)
diff --git a/openedx/core/djangoapps/programs/tests/test_tasks.py b/openedx/core/djangoapps/programs/tests/test_tasks.py
index 0f43629ba9..911489d03a 100644
--- a/openedx/core/djangoapps/programs/tests/test_tasks.py
+++ b/openedx/core/djangoapps/programs/tests/test_tasks.py
@@ -30,6 +30,7 @@ from openedx.core.djangoapps.oauth_dispatch.tests.factories import ApplicationFa
from openedx.core.djangoapps.programs import tasks
from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory, SiteFactory
from openedx.core.djangolib.testing.utils import skip_unless_lms
+from xmodule.data import CertificatesDisplayBehaviors
log = logging.getLogger(__name__)
@@ -520,6 +521,7 @@ class AwardCourseCertificatesTestCase(CredentialsApiConfigMixin, TestCase):
self.course = CourseOverviewFactory.create(
self_paced=True, # Any option to allow the certificate to be viewable for the course
certificate_available_date=self.available_date,
+ certificates_display_behavior=CertificatesDisplayBehaviors.END_WITH_DATE
)
self.student = UserFactory.create(username='test-student')
# Instantiate the Certificate first so that the config doesn't execute issuance
diff --git a/openedx/core/djangoapps/programs/tests/test_utils.py b/openedx/core/djangoapps/programs/tests/test_utils.py
index c6f9125125..4418b9b286 100644
--- a/openedx/core/djangoapps/programs/tests/test_utils.py
+++ b/openedx/core/djangoapps/programs/tests/test_utils.py
@@ -51,6 +51,7 @@ from openedx.core.djangoapps.site_configuration.tests.factories import SiteFacto
from openedx.core.djangolib.testing.utils import skip_unless_lms
from common.djangoapps.student.tests.factories import AnonymousUserFactory, CourseEnrollmentFactory, UserFactory
from common.djangoapps.util.date_utils import strftime_localized
+from xmodule.data import CertificatesDisplayBehaviors
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.django_utils import (
ModuleStoreTestCase, SharedModuleStoreTestCase, TEST_DATA_SPLIT_MODULESTORE
@@ -499,7 +500,7 @@ class TestProgramProgressMeter(ModuleStoreTestCase):
end=two_days_ago,
self_paced=False,
certificate_available_date=tomorrow,
- certificates_display_behavior='end'
+ certificates_display_behavior=CertificatesDisplayBehaviors.END_WITH_DATE
)
third_course_run_key = str(course3.id)
@@ -607,6 +608,7 @@ class TestProgramProgressMeter(ModuleStoreTestCase):
# 3 certs, all available, program cert in the past/now
course3_overview = CourseOverview.get_from_id(course3.id)
course3_overview.certificate_available_date = yesterday
+ course3_overview.certificates_display_behavior = CertificatesDisplayBehaviors.END_WITH_DATE
course3_overview.save()
meter = ProgramProgressMeter(self.site, self.user)
self._assert_progress(
@@ -622,7 +624,7 @@ class TestProgramProgressMeter(ModuleStoreTestCase):
def test_old_course_runs(self, mock_get_programs):
"""
Test that old course runs may exist for a program which do not exist in LMS.
- In that case, continue considering the course run to've been failed by the learner
+ In that case, continue considering the course run to have been failed by the learner
"""
course_run = CourseRunFactory.create()
course = CourseFactory.create(course_runs=[course_run])
diff --git a/openedx/core/djangoapps/programs/utils.py b/openedx/core/djangoapps/programs/utils.py
index da8679bdfb..5ed84f768a 100644
--- a/openedx/core/djangoapps/programs/utils.py
+++ b/openedx/core/djangoapps/programs/utils.py
@@ -448,13 +448,7 @@ class ProgramProgressMeter:
except CourseOverview.DoesNotExist:
may_certify = True
else:
- may_certify = certificate_api.should_certificate_be_visible(
- course_overview.certificates_display_behavior,
- course_overview.certificates_show_before_end,
- course_overview.has_ended(),
- course_overview.certificate_available_date,
- course_overview.self_paced
- )
+ may_certify = certificate_api.certificates_viewable_for_course(course_overview)
if (
CertificateStatuses.is_passing_status(certificate['status'])
@@ -592,13 +586,7 @@ class ProgramDataExtender:
run_mode['upgrade_url'] = None
def _attach_course_run_may_certify(self, run_mode):
- run_mode['may_certify'] = certificate_api.should_certificate_be_visible(
- self.course_overview.certificates_display_behavior,
- self.course_overview.certificates_show_before_end,
- self.course_overview.has_ended(),
- self.course_overview.certificate_available_date,
- self.course_overview.self_paced
- )
+ run_mode['may_certify'] = certificate_api.certificates_viewable_for_course(self.course_overview)
def _attach_course_run_is_mobile_only(self, run_mode):
run_mode['is_mobile_only'] = self.mobile_only
diff --git a/openedx/features/learner_profile/tests/views/test_learner_profile.py b/openedx/features/learner_profile/tests/views/test_learner_profile.py
index 97d42a5cb9..1dbcc6dbda 100644
--- a/openedx/features/learner_profile/tests/views/test_learner_profile.py
+++ b/openedx/features/learner_profile/tests/views/test_learner_profile.py
@@ -21,6 +21,7 @@ from openedx.core.djangoapps.content.course_overviews.models import CourseOvervi
from openedx.core.djangoapps.site_configuration.tests.mixins import SiteMixin
from openedx.features.learner_profile.toggles import REDIRECT_TO_PROFILE_MICROFRONTEND
from openedx.features.learner_profile.views.learner_profile import learner_profile_context
+from xmodule.data import CertificatesDisplayBehaviors
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
@@ -222,7 +223,8 @@ class LearnerProfileViewTest(SiteMixin, UrlResetMixin, ModuleStoreTestCase):
"""
# add new course with certificate_available_date is future date.
course = CourseFactory.create(
- certificate_available_date=datetime.datetime.now() + datetime.timedelta(days=5)
+ certificate_available_date=datetime.datetime.now() + datetime.timedelta(days=5),
+ certificates_display_behavior=CertificatesDisplayBehaviors.END_WITH_DATE
)
cert = self._create_certificate(course_key=course.id)