feat: added new ora notification (#34464)
This commit is contained in:
@@ -4,6 +4,7 @@ Base setup for Notification Apps and Types.
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from .email_notifications import EmailCadence
|
||||
from common.djangoapps.student.roles import CourseInstructorRole, CourseStaffRole
|
||||
from .utils import find_app_in_normalized_apps, find_pref_in_normalized_prefs
|
||||
from ..django_comment_common.models import FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR, FORUM_ROLE_COMMUNITY_TA
|
||||
|
||||
@@ -185,6 +186,25 @@ COURSE_NOTIFICATION_TYPES = {
|
||||
'email_template': '',
|
||||
'filters': [FILTER_AUDIT_EXPIRED_USERS_WITH_NO_ROLE]
|
||||
},
|
||||
'ora_staff_notification': {
|
||||
'notification_app': 'ora',
|
||||
'name': 'ora_staff_notification',
|
||||
'is_core': False,
|
||||
'info': '',
|
||||
'web': False,
|
||||
'email': False,
|
||||
'push': False,
|
||||
'email_cadence': EmailCadence.DAILY,
|
||||
'non_editable': [],
|
||||
'content_template': _('<{p}>You have a new open response submission awaiting for review for : '
|
||||
'<{strong}>{ora_name}</{strong}></{p}>'),
|
||||
'content_context': {
|
||||
'ora_name': 'Name of ORA in course',
|
||||
},
|
||||
'email_template': '',
|
||||
'filters': [FILTER_AUDIT_EXPIRED_USERS_WITH_NO_ROLE],
|
||||
'visible_to': [CourseStaffRole.ROLE, CourseInstructorRole.ROLE]
|
||||
},
|
||||
}
|
||||
|
||||
COURSE_NOTIFICATION_APPS = {
|
||||
@@ -207,6 +227,15 @@ COURSE_NOTIFICATION_APPS = {
|
||||
'core_email_cadence': EmailCadence.DAILY,
|
||||
'non_editable': []
|
||||
},
|
||||
'ora': {
|
||||
'enabled': True,
|
||||
'core_info': _('Notifications for Open response submissions.'),
|
||||
'core_web': True,
|
||||
'core_email': True,
|
||||
'core_push': True,
|
||||
'core_email_cadence': EmailCadence.DAILY,
|
||||
'non_editable': []
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -47,3 +47,14 @@ ENABLE_NOTIFICATIONS_FILTERS = CourseWaffleFlag(f"{WAFFLE_NAMESPACE}.enable_noti
|
||||
# .. toggle_target_removal_date: 2024-06-01
|
||||
# .. toggle_tickets: INF-1145
|
||||
ENABLE_COURSEWIDE_NOTIFICATIONS = CourseWaffleFlag(f"{WAFFLE_NAMESPACE}.enable_coursewide_notifications", __name__)
|
||||
|
||||
|
||||
# .. toggle_name: notifications.enable_ora_staff_notifications
|
||||
# .. toggle_implementation: CourseWaffleFlag
|
||||
# .. toggle_default: False
|
||||
# .. toggle_description: Waffle flag to enable ORA staff notifications
|
||||
# .. toggle_use_cases: temporary, open_edx
|
||||
# .. toggle_creation_date: 2024-04-04
|
||||
# .. toggle_target_removal_date: 2024-06-04
|
||||
# .. toggle_tickets: INF-1304
|
||||
ENABLE_ORA_STAFF_NOTIFICATION = CourseWaffleFlag(f"{WAFFLE_NAMESPACE}.enable_ora_staff_notifications", __name__)
|
||||
|
||||
@@ -21,7 +21,7 @@ from openedx.core.djangoapps.notifications.audience_filters import (
|
||||
CohortAudienceFilter,
|
||||
CourseRoleAudienceFilter,
|
||||
)
|
||||
from openedx.core.djangoapps.notifications.config.waffle import ENABLE_NOTIFICATIONS
|
||||
from openedx.core.djangoapps.notifications.config.waffle import ENABLE_NOTIFICATIONS, ENABLE_ORA_STAFF_NOTIFICATION
|
||||
from openedx.core.djangoapps.notifications.models import CourseNotificationPreference
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -108,6 +108,11 @@ def generate_course_notifications(signal, sender, course_notification_data, meta
|
||||
"""
|
||||
Watches for COURSE_NOTIFICATION_REQUESTED signal and calls send_notifications task
|
||||
"""
|
||||
if (
|
||||
course_notification_data.notification_type == 'ora_staff_notification'
|
||||
and not ENABLE_ORA_STAFF_NOTIFICATION.is_enabled(course_notification_data['course_key'])
|
||||
):
|
||||
return
|
||||
|
||||
from openedx.core.djangoapps.notifications.tasks import send_notifications
|
||||
course_notification_data = course_notification_data.__dict__
|
||||
|
||||
@@ -23,7 +23,7 @@ NOTIFICATION_CHANNELS = ['web', 'push', 'email']
|
||||
ADDITIONAL_NOTIFICATION_CHANNEL_SETTINGS = ['email_cadence']
|
||||
|
||||
# Update this version when there is a change to any course specific notification type or app.
|
||||
COURSE_NOTIFICATION_CONFIG_VERSION = 8
|
||||
COURSE_NOTIFICATION_CONFIG_VERSION = 9
|
||||
|
||||
|
||||
def get_course_notification_preference_config():
|
||||
|
||||
@@ -16,6 +16,7 @@ from rest_framework import status
|
||||
from rest_framework.test import APIClient, APITestCase
|
||||
|
||||
from common.djangoapps.student.models import CourseEnrollment
|
||||
from common.djangoapps.student.roles import CourseStaffRole
|
||||
from common.djangoapps.student.tests.factories import UserFactory
|
||||
from lms.djangoapps.discussion.django_comment_client.tests.factories import RoleFactory
|
||||
from lms.djangoapps.discussion.toggles import ENABLE_REPORTED_CONTENT_NOTIFICATIONS
|
||||
@@ -296,6 +297,27 @@ class UserNotificationPreferenceAPITest(ModuleStoreTestCase):
|
||||
}
|
||||
},
|
||||
'non_editable': {}
|
||||
},
|
||||
'ora': {
|
||||
'enabled': True,
|
||||
'core_notification_types': [],
|
||||
'notification_types': {
|
||||
'ora_staff_notification': {
|
||||
'web': False,
|
||||
'email': False,
|
||||
'push': False,
|
||||
'email_cadence': 'Daily',
|
||||
'info': ''
|
||||
},
|
||||
'core': {
|
||||
'web': True,
|
||||
'email': True,
|
||||
'push': True,
|
||||
'email_cadence': 'Daily',
|
||||
'info': 'Notifications for Open response submissions.'
|
||||
}
|
||||
},
|
||||
'non_editable': {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -349,6 +371,8 @@ class UserNotificationPreferenceAPITest(ModuleStoreTestCase):
|
||||
"""
|
||||
Test get user notification preference.
|
||||
"""
|
||||
if role:
|
||||
CourseStaffRole(self.course.id).add_users(self.user)
|
||||
self.client.login(username=self.user.username, password=self.TEST_PASSWORD)
|
||||
|
||||
role_instance = None
|
||||
@@ -576,6 +600,27 @@ class UserNotificationChannelPreferenceAPITest(ModuleStoreTestCase):
|
||||
}
|
||||
},
|
||||
'non_editable': {}
|
||||
},
|
||||
'ora': {
|
||||
'enabled': True,
|
||||
'core_notification_types': [],
|
||||
'notification_types': {
|
||||
'ora_staff_notification': {
|
||||
'web': False,
|
||||
'email': False,
|
||||
'push': False,
|
||||
'email_cadence': 'Daily',
|
||||
'info': ''
|
||||
},
|
||||
'core': {
|
||||
'web': True,
|
||||
'email': True,
|
||||
'push': True,
|
||||
'email_cadence': 'Daily',
|
||||
'info': 'Notifications for Open response submissions.'
|
||||
}
|
||||
},
|
||||
'non_editable': {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -881,7 +926,7 @@ class NotificationCountViewSetTestCase(ModuleStoreTestCase):
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.data['count'], 4)
|
||||
self.assertEqual(response.data['count_by_app_name'], {
|
||||
'App Name 1': 2, 'App Name 2': 1, 'App Name 3': 1, 'discussion': 0, 'updates': 0})
|
||||
'App Name 1': 2, 'App Name 2': 1, 'App Name 3': 1, 'discussion': 0, 'updates': 0, 'ora': 0})
|
||||
self.assertEqual(response.data['show_notifications_tray'], show_notifications_tray_enabled)
|
||||
|
||||
def test_get_unseen_notifications_count_for_unauthenticated_user(self):
|
||||
@@ -902,7 +947,7 @@ class NotificationCountViewSetTestCase(ModuleStoreTestCase):
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.data['count'], 0)
|
||||
self.assertEqual(response.data['count_by_app_name'], {'discussion': 0, 'updates': 0})
|
||||
self.assertEqual(response.data['count_by_app_name'], {'discussion': 0, 'updates': 0, 'ora': 0})
|
||||
|
||||
def test_get_expiry_days_in_count_view(self):
|
||||
"""
|
||||
@@ -1065,8 +1110,11 @@ def remove_notifications_with_visibility_settings(expected_response):
|
||||
Remove notifications with visibility settings from the expected response.
|
||||
"""
|
||||
not_visible = get_notification_types_with_visibility_settings()
|
||||
for notification_type, visibility_settings in not_visible.items():
|
||||
expected_response['notification_preference_config']['discussion']['notification_types'].pop(
|
||||
notification_type
|
||||
)
|
||||
for expected_response_app in expected_response['notification_preference_config']:
|
||||
for notification_type, visibility_settings in not_visible.items():
|
||||
types = expected_response['notification_preference_config'][expected_response_app]['notification_types']
|
||||
if notification_type in types:
|
||||
expected_response['notification_preference_config'][expected_response_app]['notification_types'].pop(
|
||||
notification_type
|
||||
)
|
||||
return expected_response
|
||||
|
||||
@@ -3,7 +3,7 @@ Utils function for notifications app
|
||||
"""
|
||||
from typing import Dict, List
|
||||
|
||||
from common.djangoapps.student.models import CourseEnrollment
|
||||
from common.djangoapps.student.models import CourseEnrollment, CourseAccessRole
|
||||
from lms.djangoapps.discussion.toggles import ENABLE_REPORTED_CONTENT_NOTIFICATIONS
|
||||
from openedx.core.djangoapps.django_comment_common.models import Role
|
||||
from openedx.core.lib.cache_utils import request_cached
|
||||
@@ -110,7 +110,8 @@ def get_notification_types_with_visibility_settings() -> Dict[str, List[str]]:
|
||||
def filter_out_visible_notifications(
|
||||
user_preferences: dict,
|
||||
notifications_with_visibility: Dict[str, List[str]],
|
||||
user_forum_roles: List[str]
|
||||
user_forum_roles: List[str],
|
||||
user_course_roles: List[str]
|
||||
) -> dict:
|
||||
"""
|
||||
Filter out notifications visible to forum roles from user preferences.
|
||||
@@ -119,21 +120,22 @@ def filter_out_visible_notifications(
|
||||
:param notifications_with_visibility: List of dictionaries with notification type names and
|
||||
corresponding visibility settings
|
||||
:param user_forum_roles: List of forum roles for the user
|
||||
:param user_course_roles: List of course roles for the user
|
||||
:return: Updated user preferences dictionary
|
||||
"""
|
||||
discussion_user_preferences = user_preferences.get('discussion', {})
|
||||
if 'notification_types' in discussion_user_preferences:
|
||||
# Iterate over the types to remove and pop them from the dictionary
|
||||
for notification_type, is_visible_to in notifications_with_visibility.items():
|
||||
is_visible = False
|
||||
for role in is_visible_to:
|
||||
if role in user_forum_roles:
|
||||
is_visible = True
|
||||
break
|
||||
if is_visible:
|
||||
continue
|
||||
|
||||
discussion_user_preferences['notification_types'].pop(notification_type)
|
||||
for user_preferences_app, app_config in user_preferences.items():
|
||||
if 'notification_types' in app_config:
|
||||
# Iterate over the types to remove and pop them from the dictionary
|
||||
for notification_type, is_visible_to in notifications_with_visibility.items():
|
||||
is_visible = False
|
||||
for role in is_visible_to:
|
||||
if role in user_forum_roles or role in user_course_roles:
|
||||
is_visible = True
|
||||
break
|
||||
if is_visible:
|
||||
continue
|
||||
if notification_type in user_preferences[user_preferences_app]['notification_types']:
|
||||
user_preferences[user_preferences_app]['notification_types'].pop(notification_type)
|
||||
return user_preferences
|
||||
|
||||
|
||||
@@ -148,10 +150,15 @@ def remove_preferences_with_no_access(preferences: dict, user) -> dict:
|
||||
user_preferences = preferences['notification_preference_config']
|
||||
user_forum_roles = get_user_forum_roles(user.id, preferences['course_id'])
|
||||
notifications_with_visibility_settings = get_notification_types_with_visibility_settings()
|
||||
user_course_roles = CourseAccessRole.objects.filter(
|
||||
user=user,
|
||||
course_id=preferences['course_id']
|
||||
).values_list('role', flat=True)
|
||||
preferences['notification_preference_config'] = filter_out_visible_notifications(
|
||||
user_preferences,
|
||||
notifications_with_visibility_settings,
|
||||
user_forum_roles
|
||||
user_forum_roles,
|
||||
user_course_roles
|
||||
)
|
||||
return preferences
|
||||
|
||||
|
||||
Reference in New Issue
Block a user