From 04c2d9dbbd9627fb1bff7b28eec33357e39b331c Mon Sep 17 00:00:00 2001 From: asajjad2 Date: Thu, 17 Jul 2025 17:57:33 +0500 Subject: [PATCH 1/5] fix: make Discovery service dependency optional for discussion notifications test: patch CatalogIntegration.is_enabled in expiration tests to restore mock behavior test: patch CatalogIntegration.is_enabled in setUp/tearDown feat: make course duration limits configurable refactor: clears ambigous name fix: remove unsued env vars --- lms/envs/common.py | 12 ++++++++ .../djangoapps/course_date_signals/utils.py | 30 ++++++++++++++----- .../notifications/tests/test_filters.py | 11 +++++++ .../tests/test_course_expiration.py | 7 +++++ 4 files changed, 52 insertions(+), 8 deletions(-) diff --git a/lms/envs/common.py b/lms/envs/common.py index 27e0306711e1..afe177b4b916 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -709,6 +709,18 @@ # .. setting_description: Sets the number of days after which the gradebook will freeze following the course's end. GRADEBOOK_FREEZE_DAYS = 30 +# .. setting_name: COURSE_ACCESS_DURATION_MIN_WEEKS +# .. setting_default: 4 +# .. setting_description: Minimum course duration in weeks when Discovery service data is unavailable or course has no +# .. weeks_to_complete value. Used as fallback for course access duration calculations. +COURSE_ACCESS_DURATION_MIN_WEEKS = 4 + +# .. setting_name: COURSE_ACCESS_DURATION_MAX_WEEKS +# .. setting_default: 18 +# .. setting_description: Maximum course duration in weeks. Course access duration is bounded by this upper limit +# .. regardless of Discovery service data. +COURSE_ACCESS_DURATION_MAX_WEEKS = 18 + RETRY_CALENDAR_SYNC_EMAIL_MAX_ATTEMPTS = 5 ############################# SET PATH INFORMATION ############################# diff --git a/openedx/core/djangoapps/course_date_signals/utils.py b/openedx/core/djangoapps/course_date_signals/utils.py index 78447f32610c..5f6a33b793a2 100644 --- a/openedx/core/djangoapps/course_date_signals/utils.py +++ b/openedx/core/djangoapps/course_date_signals/utils.py @@ -6,11 +6,24 @@ from datetime import timedelta +from django.conf import settings from openedx.core.djangoapps.catalog.utils import get_course_run_details +from openedx.core.djangoapps.catalog.models import CatalogIntegration +MIN_DURATION = timedelta( + weeks=getattr(settings, 'COURSE_DURATION_MIN_WEEKS', 4) +) +MAX_DURATION = timedelta( + weeks=getattr(settings, 'COURSE_DURATION_MAX_WEEKS', 18) +) -MIN_DURATION = timedelta(weeks=4) -MAX_DURATION = timedelta(weeks=18) + +def catalog_integration_enabled(): + """ + Check if catalog integration is enabled + """ + catalog_integration = CatalogIntegration.current() + return catalog_integration.is_enabled() def get_expected_duration(course_id): @@ -20,12 +33,13 @@ def get_expected_duration(course_id): access_duration = MIN_DURATION - # The user course expiration date is the content availability date - # plus the weeks_to_complete field from course-discovery. - discovery_course_details = get_course_run_details(course_id, ['weeks_to_complete']) - expected_weeks = discovery_course_details.get('weeks_to_complete') - if expected_weeks: - access_duration = timedelta(weeks=expected_weeks) + if catalog_integration_enabled(): + discovery_course_details = get_course_run_details( + course_id, ['weeks_to_complete'] + ) + expected_weeks = discovery_course_details.get('weeks_to_complete') + if expected_weeks: + access_duration = timedelta(weeks=expected_weeks) # Course access duration is bounded by the min and max duration. access_duration = max(MIN_DURATION, min(MAX_DURATION, access_duration)) diff --git a/openedx/core/djangoapps/notifications/tests/test_filters.py b/openedx/core/djangoapps/notifications/tests/test_filters.py index c8e0bdfccfb7..4d645438df52 100644 --- a/openedx/core/djangoapps/notifications/tests/test_filters.py +++ b/openedx/core/djangoapps/notifications/tests/test_filters.py @@ -3,6 +3,7 @@ """ from datetime import timedelta from unittest import mock +from unittest.mock import patch import ddt from django.utils.timezone import now @@ -43,6 +44,12 @@ class CourseExpirationTestCase(ModuleStoreTestCase): def setUp(self): super().setUp() # lint-amnesty, pylint: disable=super-with-arguments + self.catalog_patch = patch( + 'openedx.core.djangoapps.catalog.models.CatalogIntegration.is_enabled', + return_value=True + ) + self.catalog_patch.start() + self.course = CourseFactory( start=now() - timedelta(weeks=10), ) @@ -58,6 +65,10 @@ def setUp(self): expired_audit.created = now() - timedelta(weeks=6) expired_audit.save() + def tearDown(self): + self.catalog_patch.stop() + super().tearDown() + @mock.patch("openedx.core.djangoapps.course_date_signals.utils.get_course_run_details") def test_audit_expired_filter_with_no_role( self, diff --git a/openedx/features/course_duration_limits/tests/test_course_expiration.py b/openedx/features/course_duration_limits/tests/test_course_expiration.py index b794ef3076c4..de7c1d20d768 100644 --- a/openedx/features/course_duration_limits/tests/test_course_expiration.py +++ b/openedx/features/course_duration_limits/tests/test_course_expiration.py @@ -4,6 +4,7 @@ from datetime import timedelta from unittest import mock +from unittest.mock import patch import ddt from django.conf import settings @@ -46,6 +47,11 @@ class CourseExpirationTestCase(ModuleStoreTestCase, MasqueradeMixin): """Tests to verify the get_user_course_expiration_date function is working correctly""" def setUp(self): super().setUp() # lint-amnesty, pylint: disable=super-with-arguments + self.catalog_patch = patch( + 'openedx.core.djangoapps.catalog.models.CatalogIntegration.is_enabled', + return_value=True + ) + self.catalog_patch.start() self.course = CourseFactory( start=now() - timedelta(weeks=10), ) @@ -72,6 +78,7 @@ def setUp(self): add_course_mode(self.course) def tearDown(self): + self.catalog_patch.stop() CourseEnrollment.unenroll(self.user, self.course.id) super().tearDown() # lint-amnesty, pylint: disable=super-with-arguments From 8b7e9b7feeaec19145994c6909d3b930fb7e2136 Mon Sep 17 00:00:00 2001 From: asajjad2 Date: Tue, 10 Mar 2026 17:12:06 +0500 Subject: [PATCH 2/5] fix: correct setting name --- openedx/core/djangoapps/course_date_signals/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openedx/core/djangoapps/course_date_signals/utils.py b/openedx/core/djangoapps/course_date_signals/utils.py index 5f6a33b793a2..814c2a980746 100644 --- a/openedx/core/djangoapps/course_date_signals/utils.py +++ b/openedx/core/djangoapps/course_date_signals/utils.py @@ -11,10 +11,10 @@ from openedx.core.djangoapps.catalog.models import CatalogIntegration MIN_DURATION = timedelta( - weeks=getattr(settings, 'COURSE_DURATION_MIN_WEEKS', 4) + weeks=getattr(settings, 'COURSE_ACCESS_DURATION_MIN_WEEKS', 4) ) MAX_DURATION = timedelta( - weeks=getattr(settings, 'COURSE_DURATION_MAX_WEEKS', 18) + weeks=getattr(settings, 'COURSE_ACCESS_DURATION_MAX_WEEKS', 18) ) From a02e09d7e43491818fc51441dc7c218d1246f672 Mon Sep 17 00:00:00 2001 From: asajjad2 Date: Tue, 10 Mar 2026 17:15:09 +0500 Subject: [PATCH 3/5] refactor: accessing variables --- openedx/core/djangoapps/course_date_signals/utils.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/openedx/core/djangoapps/course_date_signals/utils.py b/openedx/core/djangoapps/course_date_signals/utils.py index 814c2a980746..9e29eab790b2 100644 --- a/openedx/core/djangoapps/course_date_signals/utils.py +++ b/openedx/core/djangoapps/course_date_signals/utils.py @@ -10,12 +10,8 @@ from openedx.core.djangoapps.catalog.utils import get_course_run_details from openedx.core.djangoapps.catalog.models import CatalogIntegration -MIN_DURATION = timedelta( - weeks=getattr(settings, 'COURSE_ACCESS_DURATION_MIN_WEEKS', 4) -) -MAX_DURATION = timedelta( - weeks=getattr(settings, 'COURSE_ACCESS_DURATION_MAX_WEEKS', 18) -) +MIN_DURATION = timedelta(weeks=settings.COURSE_ACCESS_DURATION_MIN_WEEKS) +MAX_DURATION = timedelta(weeks=settings.COURSE_ACCESS_DURATION_MAX_WEEKS) def catalog_integration_enabled(): From 621fae8277ee02b325ea7b9223317b6bb1a001ed Mon Sep 17 00:00:00 2001 From: asajjad2 Date: Tue, 10 Mar 2026 17:36:03 +0500 Subject: [PATCH 4/5] refactor: move settings to openedx/common --- lms/envs/common.py | 12 ------------ openedx/envs/common.py | 13 +++++++++++++ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/lms/envs/common.py b/lms/envs/common.py index afe177b4b916..27e0306711e1 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -709,18 +709,6 @@ # .. setting_description: Sets the number of days after which the gradebook will freeze following the course's end. GRADEBOOK_FREEZE_DAYS = 30 -# .. setting_name: COURSE_ACCESS_DURATION_MIN_WEEKS -# .. setting_default: 4 -# .. setting_description: Minimum course duration in weeks when Discovery service data is unavailable or course has no -# .. weeks_to_complete value. Used as fallback for course access duration calculations. -COURSE_ACCESS_DURATION_MIN_WEEKS = 4 - -# .. setting_name: COURSE_ACCESS_DURATION_MAX_WEEKS -# .. setting_default: 18 -# .. setting_description: Maximum course duration in weeks. Course access duration is bounded by this upper limit -# .. regardless of Discovery service data. -COURSE_ACCESS_DURATION_MAX_WEEKS = 18 - RETRY_CALENDAR_SYNC_EMAIL_MAX_ATTEMPTS = 5 ############################# SET PATH INFORMATION ############################# diff --git a/openedx/envs/common.py b/openedx/envs/common.py index 240f41f901ae..b30f8c428d31 100644 --- a/openedx/envs/common.py +++ b/openedx/envs/common.py @@ -743,6 +743,19 @@ def make_mako_module_dir(settings): LANGUAGE_DICT = dict(LANGUAGES) +# .. setting_name: COURSE_ACCESS_DURATION_MIN_WEEKS +# .. setting_default: 4 +# .. setting_description: Minimum course duration in weeks when Discovery service data is unavailable or course has no +# .. weeks_to_complete value. Used as fallback for course access duration calculations (e.g., audit access expiration +# .. and discussion notification filtering). +COURSE_ACCESS_DURATION_MIN_WEEKS = 4 + +# .. setting_name: COURSE_ACCESS_DURATION_MAX_WEEKS +# .. setting_default: 18 +# .. setting_description: Maximum course duration in weeks. Course access duration is bounded by this upper limit +# .. regardless of Discovery service data. +COURSE_ACCESS_DURATION_MAX_WEEKS = 18 + ############################## Optional Apps ############################### OPTIONAL_APPS = [ From 89299b17098fec4577fd833212cfbba05b59fb6c Mon Sep 17 00:00:00 2001 From: asajjad2 Date: Wed, 11 Mar 2026 16:22:50 +0500 Subject: [PATCH 5/5] refactor: mark function as private --- openedx/core/djangoapps/course_date_signals/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openedx/core/djangoapps/course_date_signals/utils.py b/openedx/core/djangoapps/course_date_signals/utils.py index 9e29eab790b2..21789cdcfd7f 100644 --- a/openedx/core/djangoapps/course_date_signals/utils.py +++ b/openedx/core/djangoapps/course_date_signals/utils.py @@ -14,7 +14,7 @@ MAX_DURATION = timedelta(weeks=settings.COURSE_ACCESS_DURATION_MAX_WEEKS) -def catalog_integration_enabled(): +def _catalog_integration_enabled(): """ Check if catalog integration is enabled """ @@ -29,7 +29,7 @@ def get_expected_duration(course_id): access_duration = MIN_DURATION - if catalog_integration_enabled(): + if _catalog_integration_enabled(): discovery_course_details = get_course_run_details( course_id, ['weeks_to_complete'] )