Skip to content

Commit cfe4059

Browse files
feat: added API to get account level preferences (#36957)
* feat: added API to get account-level preferences * feat: added update api for account level preferences (#36978)
1 parent a0bb77a commit cfe4059

4 files changed

Lines changed: 511 additions & 7 deletions

File tree

openedx/core/djangoapps/notifications/tasks.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,14 @@
2121
get_default_values_of_preference,
2222
get_notification_content
2323
)
24+
25+
from openedx.core.djangoapps.notifications.email.tasks import send_immediate_cadence_email
2426
from openedx.core.djangoapps.notifications.config.waffle import (
25-
ENABLE_ACCOUNT_LEVEL_PREFERENCES,
2627
ENABLE_NOTIFICATION_GROUPING,
2728
ENABLE_NOTIFICATIONS,
29+
ENABLE_ACCOUNT_LEVEL_PREFERENCES,
2830
ENABLE_PUSH_NOTIFICATIONS
2931
)
30-
from openedx.core.djangoapps.notifications.email.tasks import send_immediate_cadence_email
3132
from openedx.core.djangoapps.notifications.email_notifications import EmailCadence
3233
from openedx.core.djangoapps.notifications.events import notification_generated_event
3334
from openedx.core.djangoapps.notifications.grouping_notifications import (

openedx/core/djangoapps/notifications/tests/test_views.py

Lines changed: 306 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,16 @@
3636
from openedx.core.djangoapps.notifications.models import (
3737
CourseNotificationPreference,
3838
Notification,
39-
get_course_notification_preference_config_version
39+
get_course_notification_preference_config_version, NotificationPreference
4040
)
4141
from openedx.core.djangoapps.notifications.serializers import NotificationCourseEnrollmentSerializer
4242
from openedx.core.djangoapps.user_api.models import UserPreference
4343
from openedx.core.djangoapps.notifications.email.utils import update_user_preferences_from_patch
4444
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
4545
from xmodule.modulestore.tests.factories import CourseFactory
4646

47-
from ..base_notification import COURSE_NOTIFICATION_APPS, COURSE_NOTIFICATION_TYPES, NotificationAppManager
47+
from ..base_notification import COURSE_NOTIFICATION_APPS, COURSE_NOTIFICATION_TYPES, NotificationAppManager, \
48+
NotificationTypeManager
4849
from ..utils import get_notification_types_with_visibility_settings
4950

5051
User = get_user_model()
@@ -1414,3 +1415,306 @@ def test_non_editable_is_added_in_api_response(self):
14141415
prefs = response.data['data']
14151416
self.assertDictEqual(prefs['updates']['non_editable'], {'course_updates': ['email']})
14161417
self.assertDictEqual(prefs['discussion']['non_editable'], {'core': ['web']})
1418+
1419+
1420+
class TestNotificationPreferencesView(APITestCase):
1421+
"""
1422+
Tests for the NotificationPreferencesView API view.
1423+
"""
1424+
1425+
def setUp(self):
1426+
# Set up a user and API client
1427+
self.default_data = {
1428+
"status": "success",
1429+
"message": "Notification preferences retrieved successfully.",
1430+
"data": {
1431+
"discussion": {
1432+
"enabled": True,
1433+
"core_notification_types": [
1434+
"new_comment_on_response",
1435+
"new_comment",
1436+
"new_response",
1437+
"response_on_followed_post",
1438+
"comment_on_followed_post",
1439+
"response_endorsed_on_thread",
1440+
"response_endorsed"
1441+
],
1442+
"notification_types": {
1443+
"new_discussion_post": {
1444+
"web": False,
1445+
"email": False,
1446+
"push": False,
1447+
"email_cadence": "Daily"
1448+
},
1449+
"new_question_post": {
1450+
"web": False,
1451+
"email": False,
1452+
"push": False,
1453+
"email_cadence": "Daily"
1454+
},
1455+
"content_reported": {
1456+
"web": True,
1457+
"email": True,
1458+
"push": True,
1459+
"email_cadence": "Daily"
1460+
},
1461+
"new_instructor_all_learners_post": {
1462+
"web": True,
1463+
"email": False,
1464+
"push": False,
1465+
"email_cadence": "Daily"
1466+
},
1467+
"core": {
1468+
"web": True,
1469+
"email": True,
1470+
"push": True,
1471+
"email_cadence": "Daily"
1472+
}
1473+
},
1474+
"non_editable": {}
1475+
},
1476+
"updates": {
1477+
"enabled": True,
1478+
"core_notification_types": [],
1479+
"notification_types": {
1480+
"course_updates": {
1481+
"web": True,
1482+
"email": False,
1483+
"push": True,
1484+
"email_cadence": "Daily"
1485+
},
1486+
"core": {
1487+
"web": True,
1488+
"email": True,
1489+
"push": True,
1490+
"email_cadence": "Daily"
1491+
}
1492+
},
1493+
"non_editable": {}
1494+
},
1495+
"grading": {
1496+
"enabled": True,
1497+
"core_notification_types": [],
1498+
"notification_types": {
1499+
"ora_staff_notifications": {
1500+
"web": True,
1501+
"email": False,
1502+
"push": False,
1503+
"email_cadence": "Daily"
1504+
},
1505+
"ora_grade_assigned": {
1506+
"web": True,
1507+
"email": True,
1508+
"push": False,
1509+
"email_cadence": "Daily"
1510+
},
1511+
"core": {
1512+
"web": True,
1513+
"email": True,
1514+
"push": True,
1515+
"email_cadence": "Daily"
1516+
}
1517+
},
1518+
"non_editable": {}
1519+
}
1520+
}
1521+
}
1522+
self.user = User.objects.create_user(username='testuser', password='testpass')
1523+
self.client = APIClient()
1524+
self.client.force_authenticate(user=self.user)
1525+
self.url = reverse('notification-preferences-aggregated-v2') # Adjust with the actual name
1526+
1527+
def test_get_notification_preferences(self):
1528+
"""
1529+
Test case: Get notification preferences for the authenticated user
1530+
"""
1531+
response = self.client.get(self.url)
1532+
self.assertEqual(response.status_code, status.HTTP_200_OK)
1533+
self.assertEqual(response.data['status'], 'success')
1534+
self.assertIn('data', response.data)
1535+
self.assertEqual(response.data['data'], self.default_data['data'])
1536+
1537+
def test_if_data_is_correctly_aggregated(self):
1538+
"""
1539+
Test case: Check if the data is correctly formatted
1540+
"""
1541+
1542+
self.client.get(self.url)
1543+
NotificationPreference.objects.all().update(
1544+
web=False,
1545+
push=False,
1546+
email=False,
1547+
)
1548+
response = self.client.get(self.url)
1549+
self.assertEqual(response.status_code, status.HTTP_200_OK)
1550+
self.assertEqual(response.data['status'], 'success')
1551+
self.assertIn('data', response.data)
1552+
data = {
1553+
"status": "success",
1554+
"message": "Notification preferences retrieved successfully.",
1555+
"data": {
1556+
"discussion": {
1557+
"enabled": True,
1558+
"core_notification_types": [
1559+
"new_comment_on_response",
1560+
"new_comment",
1561+
"new_response",
1562+
"response_on_followed_post",
1563+
"comment_on_followed_post",
1564+
"response_endorsed_on_thread",
1565+
"response_endorsed"
1566+
],
1567+
"notification_types": {
1568+
"new_discussion_post": {
1569+
"web": False,
1570+
"email": False,
1571+
"push": False,
1572+
"email_cadence": "Daily"
1573+
},
1574+
"new_question_post": {
1575+
"web": False,
1576+
"email": False,
1577+
"push": False,
1578+
"email_cadence": "Daily"
1579+
},
1580+
"content_reported": {
1581+
"web": False,
1582+
"email": False,
1583+
"push": False,
1584+
"email_cadence": "Daily"
1585+
},
1586+
"new_instructor_all_learners_post": {
1587+
"web": False,
1588+
"email": False,
1589+
"push": False,
1590+
"email_cadence": "Daily"
1591+
},
1592+
"core": {
1593+
"web": False,
1594+
"email": False,
1595+
"push": False,
1596+
"email_cadence": "Daily"
1597+
}
1598+
},
1599+
"non_editable": {}
1600+
},
1601+
"updates": {
1602+
"enabled": True,
1603+
"core_notification_types": [],
1604+
"notification_types": {
1605+
"course_updates": {
1606+
"web": False,
1607+
"email": False,
1608+
"push": False,
1609+
"email_cadence": "Daily"
1610+
},
1611+
"core": {
1612+
"web": True,
1613+
"email": True,
1614+
"push": True,
1615+
"email_cadence": "Daily"
1616+
}
1617+
},
1618+
"non_editable": {}
1619+
},
1620+
"grading": {
1621+
"enabled": True,
1622+
"core_notification_types": [],
1623+
"notification_types": {
1624+
"ora_staff_notifications": {
1625+
"web": False,
1626+
"email": False,
1627+
"push": False,
1628+
"email_cadence": "Daily"
1629+
},
1630+
"ora_grade_assigned": {
1631+
"web": False,
1632+
"email": False,
1633+
"push": False,
1634+
"email_cadence": "Daily"
1635+
},
1636+
"core": {
1637+
"web": True,
1638+
"email": True,
1639+
"push": True,
1640+
"email_cadence": "Daily"
1641+
}
1642+
},
1643+
"non_editable": {}
1644+
}
1645+
}
1646+
}
1647+
self.assertEqual(response.data, data)
1648+
1649+
def test_api_view_permissions(self):
1650+
"""
1651+
Test case: Ensure the API view has the correct permissions
1652+
"""
1653+
# Check if the view requires authentication
1654+
self.client.logout()
1655+
response = self.client.get(self.url)
1656+
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
1657+
1658+
# Re-authenticate and check again
1659+
self.client.force_authenticate(user=self.user)
1660+
response = self.client.get(self.url)
1661+
self.assertEqual(response.status_code, status.HTTP_200_OK)
1662+
1663+
def test_update_preferences_core(self):
1664+
"""
1665+
Test case: Update notification preferences for the authenticated user
1666+
"""
1667+
update_data = {
1668+
"notification_app": "discussion",
1669+
"notification_type": "core",
1670+
"notification_channel": "email_cadence",
1671+
"email_cadence": "Weekly"
1672+
}
1673+
__, core_types = NotificationTypeManager().get_notification_app_preference('discussion')
1674+
self.client.get(self.url)
1675+
response = self.client.put(self.url, update_data, format='json')
1676+
self.assertEqual(response.status_code, status.HTTP_200_OK)
1677+
self.assertEqual(response.data['status'], 'success')
1678+
cadence_set = NotificationPreference.objects.filter(type__in=core_types).values_list('email_cadence', flat=True)
1679+
self.assertEqual(len(set(cadence_set)), 1)
1680+
self.assertIn('Weekly', set(cadence_set))
1681+
1682+
def test_update_preferences(self):
1683+
"""
1684+
Test case: Update notification preferences for the authenticated user
1685+
"""
1686+
update_data = {
1687+
"notification_app": "discussion",
1688+
"notification_type": "new_discussion_post",
1689+
"notification_channel": "web",
1690+
"value": True
1691+
}
1692+
self.client.get(self.url)
1693+
response = self.client.put(self.url, update_data, format='json')
1694+
self.assertEqual(response.status_code, status.HTTP_200_OK)
1695+
self.assertEqual(response.data['status'], 'success')
1696+
preference = NotificationPreference.objects.get(
1697+
type='new_discussion_post',
1698+
user__id=self.user.id
1699+
)
1700+
self.assertEqual(preference.web, True)
1701+
1702+
def test_update_preferences_non_core_email(self):
1703+
"""
1704+
Test case: Update notification preferences for the authenticated user
1705+
"""
1706+
update_data = {
1707+
"notification_app": "discussion",
1708+
"notification_type": "new_discussion_post",
1709+
"notification_channel": "email_cadence",
1710+
"email_cadence": 'Weekly'
1711+
}
1712+
self.client.get(self.url)
1713+
response = self.client.put(self.url, update_data, format='json')
1714+
self.assertEqual(response.status_code, status.HTTP_200_OK)
1715+
self.assertEqual(response.data['status'], 'success')
1716+
preference = NotificationPreference.objects.get(
1717+
type='new_discussion_post',
1718+
user__id=self.user.id
1719+
)
1720+
self.assertEqual(preference.email_cadence, 'Weekly')

openedx/core/djangoapps/notifications/urls.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
NotificationReadAPIView,
1414
UpdateAllNotificationPreferencesView,
1515
UserNotificationPreferenceView,
16-
preference_update_from_encrypted_username_view, AggregatedNotificationPreferences
16+
preference_update_from_encrypted_username_view, AggregatedNotificationPreferences, NotificationPreferencesView
1717
)
1818

1919
router = routers.DefaultRouter()
@@ -30,6 +30,11 @@
3030
AggregatedNotificationPreferences.as_view(),
3131
name='notification-preferences-aggregated'
3232
),
33+
path(
34+
'v2/configurations/',
35+
NotificationPreferencesView.as_view(),
36+
name='notification-preferences-aggregated-v2'
37+
),
3338
path('', NotificationListAPIView.as_view(), name='notifications-list'),
3439
path('count/', NotificationCountView.as_view(), name='notifications-count'),
3540
path(

0 commit comments

Comments
 (0)