diff --git a/openedx/core/djangoapps/notifications/tasks.py b/openedx/core/djangoapps/notifications/tasks.py index ed681a4dee..6e758bbc50 100644 --- a/openedx/core/djangoapps/notifications/tasks.py +++ b/openedx/core/djangoapps/notifications/tasks.py @@ -11,7 +11,12 @@ from edx_django_utils.monitoring import set_code_owner_attribute from pytz import UTC from common.djangoapps.student.models import CourseEnrollment -from openedx.core.djangoapps.notifications.models import CourseNotificationPreference, Notification +from openedx.core.djangoapps.notifications.config.waffle import ENABLE_NOTIFICATIONS +from openedx.core.djangoapps.notifications.models import ( + CourseNotificationPreference, + Notification, + get_course_notification_preference_config_version +) logger = get_task_logger(__name__) @@ -78,6 +83,8 @@ def send_notifications(user_ids, course_key, app_name, notification_type, contex """ Send notifications to the users. """ + if not ENABLE_NOTIFICATIONS.is_enabled(course_key): + return user_ids = list(set(user_ids)) # check if what is preferences of user and make decision to send notification or not @@ -85,8 +92,10 @@ def send_notifications(user_ids, course_key, app_name, notification_type, contex user_id__in=user_ids, course_id=course_key, ) + preferences = create_notification_pref_if_not_exists(user_ids, preferences, course_key) notifications = [] for preference in preferences: + preference = update_user_preference(preference, preference.user, course_key) if preference and preference.get_web_config(app_name, notification_type): notifications.append(Notification( user_id=preference.user_id, @@ -98,3 +107,34 @@ def send_notifications(user_ids, course_key, app_name, notification_type, contex )) # send notification to users but use bulk_create Notification.objects.bulk_create(notifications) + + +def update_user_preference(preference: CourseNotificationPreference, user, course_id): + """ + Update user preference if config version is changed. + """ + current_version = get_course_notification_preference_config_version() + if preference.config_version != current_version: + return preference.get_updated_user_course_preferences(user, course_id) + return preference + + +def create_notification_pref_if_not_exists(user_ids, preferences, course_id): + """ + Create notification preference if not exist. + """ + new_preferences = [] + + for user_id in user_ids: + if not any(preference.user_id == user_id for preference in preferences): + new_preferences.append(CourseNotificationPreference( + user_id=user_id, + course_id=course_id, + )) + logger.info('Creating new notification preference for user because it does not exist.') + if new_preferences: + # ignoring conflicts because it is possible that preference is already created by another process + # conflicts may arise because of constraint on user_id and course_id fields in model + CourseNotificationPreference.objects.bulk_create(new_preferences, ignore_conflicts=True) + preferences = preferences + new_preferences + return preferences diff --git a/openedx/core/djangoapps/notifications/tests/test_tasks.py b/openedx/core/djangoapps/notifications/tests/test_tasks.py new file mode 100644 index 0000000000..30f8f5d2b9 --- /dev/null +++ b/openedx/core/djangoapps/notifications/tests/test_tasks.py @@ -0,0 +1,78 @@ +""" +Tests for notifications tasks. +""" +from unittest.mock import patch + +from edx_toggles.toggles.testutils import override_waffle_flag + +from common.djangoapps.student.tests.factories import UserFactory +from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase +from xmodule.modulestore.tests.factories import CourseFactory +from ..config.waffle import ENABLE_NOTIFICATIONS + +from ..models import CourseNotificationPreference +from ..tasks import create_notification_pref_if_not_exists, update_user_preference + + +@patch('openedx.core.djangoapps.notifications.models.COURSE_NOTIFICATION_CONFIG_VERSION', 1) +class TestNotificationsTasks(ModuleStoreTestCase): + """ + Tests for notifications tasks. + """ + + def setUp(self): + """ + Create a course and users for the course. + """ + + super().setUp() + self.user = UserFactory() + self.user_1 = UserFactory() + self.user_2 = UserFactory() + self.course_1 = CourseFactory.create( + org='testorg', + number='testcourse', + run='testrun' + ) + self.course_2 = CourseFactory.create( + org='testorg', + number='testcourse_2', + run='testrun' + ) + self.preference_v1 = CourseNotificationPreference.objects.create( + user_id=self.user.id, + course_id=self.course_1.id, + config_version=0, + ) + self.preference_v2 = CourseNotificationPreference.objects.create( + user_id=self.user.id, + course_id=self.course_2.id, + config_version=1, + ) + + def test_update_user_preference(self): + """ + Test whether update_user_preference updates the preference with the latest config version. + """ + # Test whether update_user_preference updates the preference with a different config version + updated_preference = update_user_preference(self.preference_v1, self.user, self.course_1.id) + self.assertEqual(updated_preference.config_version, 1) + + # Test whether update_user_preference does not update the preference if the config version is the same + updated_preference = update_user_preference(self.preference_v2, self.user, self.course_2.id) + self.assertEqual(updated_preference.config_version, 1) + + @override_waffle_flag(ENABLE_NOTIFICATIONS, active=True) + def test_create_notification_pref_if_not_exists(self): + """ + Test whether create_notification_pref_if_not_exists creates a new preference if it doesn't exist. + """ + # Test whether create_notification_pref_if_not_exists creates a new preference if it doesn't exist + user_ids = [self.user.id, self.user_1.id, self.user_2.id] + preferences = [self.preference_v2] + updated_preferences = create_notification_pref_if_not_exists(user_ids, preferences, self.course_2.id) + self.assertEqual(len(updated_preferences), 3) # Should have created two new preferences + + # Test whether create_notification_pref_if_not_exists doesn't create a new preference if it already exists + updated_preferences = create_notification_pref_if_not_exists(user_ids, preferences, self.course_2.id) + self.assertEqual(len(updated_preferences), 3) # No new preferences should be created this time