From 2da176ce32cab023c4b9364d57de4b2d12b5f66e Mon Sep 17 00:00:00 2001 From: SaadYousaf Date: Wed, 31 May 2023 14:16:21 +0500 Subject: [PATCH] feat: implementing basic functionality to incorporate new notification types --- .../core/djangoapps/notifications/admin.py | 17 +- .../notifications/base_notification.py | 180 ++++++++++++++++++ .../core/djangoapps/notifications/handlers.py | 9 +- .../migrations/0004_auto_20230526_1213.py | 24 --- .../migrations/0004_auto_20230607_0757.py | 63 ++++++ .../core/djangoapps/notifications/models.py | 117 ++++++------ .../djangoapps/notifications/serializers.py | 7 +- .../notifications/tests/test_views.py | 54 ++++-- .../core/djangoapps/notifications/views.py | 21 +- 9 files changed, 374 insertions(+), 118 deletions(-) create mode 100644 openedx/core/djangoapps/notifications/base_notification.py delete mode 100644 openedx/core/djangoapps/notifications/migrations/0004_auto_20230526_1213.py create mode 100644 openedx/core/djangoapps/notifications/migrations/0004_auto_20230607_0757.py diff --git a/openedx/core/djangoapps/notifications/admin.py b/openedx/core/djangoapps/notifications/admin.py index a4797e0701..2a7f13e60b 100644 --- a/openedx/core/djangoapps/notifications/admin.py +++ b/openedx/core/djangoapps/notifications/admin.py @@ -4,15 +4,24 @@ Django Admin for Notifications from django.contrib import admin -from .models import Notification, NotificationPreference +from .models import Notification, CourseNotificationPreference class NotificationAdmin(admin.ModelAdmin): pass -class NotificationPreferenceAdmin(admin.ModelAdmin): - pass +class CourseNotificationPreferenceAdmin(admin.ModelAdmin): + """ + Admin for Course Notification Preferences + """ + model = CourseNotificationPreference + list_display = ['get_username', 'course_id', 'notification_preference_config'] + + @admin.display(description='Username', ordering='user__username') + def get_username(self, obj): + return obj.user.username + admin.site.register(Notification, NotificationAdmin) -admin.site.register(NotificationPreference, NotificationPreferenceAdmin) +admin.site.register(CourseNotificationPreference, CourseNotificationPreferenceAdmin) diff --git a/openedx/core/djangoapps/notifications/base_notification.py b/openedx/core/djangoapps/notifications/base_notification.py new file mode 100644 index 0000000000..6385b5875e --- /dev/null +++ b/openedx/core/djangoapps/notifications/base_notification.py @@ -0,0 +1,180 @@ +""" +Base setup for Notification Apps and Types. +""" +from typing import Dict + +COURSE_NOTIFICATION_TYPES = { + 'new_comment_on_response': { + 'notification_app': 'discussion', + 'name': 'new_comment_on_response', + 'is_core': True, + 'info': 'Comment on response', + 'content_template': '

{replier_name} replied on your response in ' + '{post_title}

', + 'content_context': { + 'post_title': 'Post title', + 'replier_name': 'replier name', + }, + 'email_template': '', + }, + 'new_comment': { + 'notification_app': 'discussion', + 'name': 'new_comment', + 'is_core': False, + 'web': True, + 'email': True, + 'push': True, + 'info': 'Comment on post', + 'non-editable': ['web', 'email'], + 'content_template': '

{replier_name} replied on {author_name} response ' + 'to your post {post_title}

', + 'content_context': { + 'post_title': 'Post title', + 'author_name': 'author name', + 'replier_name': 'replier name', + }, + 'email_template': '', + }, + 'new_response': { + 'notification_app': 'discussion', + 'name': 'new_response', + 'is_core': False, + 'web': True, + 'email': True, + 'push': True, + 'info': 'Response on post', + 'non-editable': [], + 'content_template': '

{replier_name} responded to your ' + 'post {post_title}

', + 'content_context': { + 'post_title': 'Post title', + 'replier_name': 'replier name', + }, + 'email_template': '', + }, +} + +COURSE_NOTIFICATION_APPS = { + 'discussion': { + 'enabled': True, + 'core_info': '', + 'core_web': True, + 'core_email': True, + 'core_push': True, + } +} + + +class NotificationTypeManager: + """ + Manager for notification types + """ + notification_types: Dict = {} + + def __init__(self): + self.notification_types = COURSE_NOTIFICATION_TYPES + + def get_notification_types_by_app(self, notification_app): + """ + Returns notification types for the given notification app. + """ + return [ + notification_type for _, notification_type in self.notification_types.items() + if notification_type.get('notification_app', None) == notification_app + ] + + def get_core_and_non_core_notification_types(self, notification_app): + """ + Returns core notification types for the given app name. + """ + notification_types = self.get_notification_types_by_app(notification_app) + core_notification_types = [] + non_core_notification_types = [] + for notification_type in notification_types: + if notification_type.get('is_core', None): + core_notification_types.append(notification_type) + else: + non_core_notification_types.append(notification_type) + return core_notification_types, non_core_notification_types + + @staticmethod + def get_non_editable_notification_channels(notification_types): + """ + Returns non-editable notification channels for the given notification types. + """ + non_editable_notification_channels = {} + for notification_type in notification_types: + if notification_type.get('non-editable', None): + non_editable_notification_channels[notification_type.get('name')] = \ + notification_type.get('non-editable') + return non_editable_notification_channels + + @staticmethod + def get_non_core_notification_type_preferences(non_core_notification_types): + """ + Returns non-core notification type preferences for the given notification types. + """ + non_core_notification_type_preferences = {} + for notification_type in non_core_notification_types: + non_core_notification_type_preferences[notification_type.get('name')] = { + 'web': notification_type.get('web', False), + 'email': notification_type.get('email', False), + 'push': notification_type.get('push', False), + 'info': notification_type.get('info', ''), + } + return non_core_notification_type_preferences + + def get_notification_app_preference(self, notification_app): + """ + Returns notification app preferences for the given notification app. + """ + core_notification_types, non_core_notification_types = self.get_core_and_non_core_notification_types( + notification_app, + ) + non_core_notification_types_preferences = self.get_non_core_notification_type_preferences( + non_core_notification_types, + ) + non_editable_notification_channels = self.get_non_editable_notification_channels(non_core_notification_types) + core_notification_types_name = [notification_type.get('name') for notification_type in core_notification_types] + return non_core_notification_types_preferences, core_notification_types_name, non_editable_notification_channels + + +class NotificationAppManager: + """ + Notification app manager + """ + notification_apps: Dict = {} + + def __init__(self): + self.notification_apps = COURSE_NOTIFICATION_APPS + + def add_core_notification_preference(self, notification_app_attrs, notification_types): + """ + Adds core notification preference for the given notification app. + """ + notification_types['core'] = { + 'web': notification_app_attrs.get('core_web', False), + 'email': notification_app_attrs.get('core_email', False), + 'push': notification_app_attrs.get('core_push', False), + 'info': notification_app_attrs.get('core_info', ''), + } + + def get_notification_app_preferences(self): + """ + Returns notification app preferences for the given name. + """ + course_notification_preference_config = {} + for notification_app_key, notification_app_attrs in COURSE_NOTIFICATION_APPS.items(): + notification_app_preferences = {} + notification_types, core_notifications, \ + non_editable_channels = NotificationTypeManager().get_notification_app_preference(notification_app_key) + self.add_core_notification_preference(notification_app_attrs, notification_types) + + notification_app_preferences['enabled'] = notification_app_attrs.get('enabled', False) + notification_app_preferences['core_notification_types'] = core_notifications + notification_app_preferences['notification_types'] = notification_types + notification_app_preferences['non_editable'] = non_editable_channels + course_notification_preference_config[notification_app_key] = notification_app_preferences + + return course_notification_preference_config + return None diff --git a/openedx/core/djangoapps/notifications/handlers.py b/openedx/core/djangoapps/notifications/handlers.py index e76622dc26..a8f6b053f6 100644 --- a/openedx/core/djangoapps/notifications/handlers.py +++ b/openedx/core/djangoapps/notifications/handlers.py @@ -8,7 +8,7 @@ from django.db.models.signals import post_save from django.dispatch import receiver from openedx.core.djangoapps.notifications.config.waffle import ENABLE_NOTIFICATIONS -from openedx.core.djangoapps.notifications.models import NotificationPreference +from openedx.core.djangoapps.notifications.models import CourseNotificationPreference log = logging.getLogger(__name__) @@ -17,10 +17,11 @@ log = logging.getLogger(__name__) def course_enrollment_post_save(sender, instance, created, **kwargs): """ Watches for post_save signal for creates on the CourseEnrollment table. - Generate a NotificationPreference if new Enrollment is created + Generate a CourseNotificationPreference if new Enrollment is created """ if created and ENABLE_NOTIFICATIONS.is_enabled(instance.course_id): try: - NotificationPreference.objects.create(user=instance.user, course_id=instance.course_id) + CourseNotificationPreference.objects.create(user=instance.user, course_id=instance.course_id) except IntegrityError: - log.info(f'NotificationPreference already exists for user {instance.user} and course {instance.course_id}') + log.info(f'CourseNotificationPreference already exists for user {instance.user} ' + f'and course {instance.course_id}') diff --git a/openedx/core/djangoapps/notifications/migrations/0004_auto_20230526_1213.py b/openedx/core/djangoapps/notifications/migrations/0004_auto_20230526_1213.py deleted file mode 100644 index 41ac15a8c7..0000000000 --- a/openedx/core/djangoapps/notifications/migrations/0004_auto_20230526_1213.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 3.2.19 on 2023-05-26 12:13 - -from django.db import migrations, models -import openedx.core.djangoapps.notifications.models - - -class Migration(migrations.Migration): - - dependencies = [ - ('notifications', '0003_alter_notification_app_name'), - ] - - operations = [ - migrations.AlterField( - model_name='notification', - name='content_context', - field=models.JSONField(default=dict), - ), - migrations.AlterField( - model_name='notificationpreference', - name='notification_preference_config', - field=models.JSONField(default=openedx.core.djangoapps.notifications.models.get_notification_preference_config), - ), - ] diff --git a/openedx/core/djangoapps/notifications/migrations/0004_auto_20230607_0757.py b/openedx/core/djangoapps/notifications/migrations/0004_auto_20230607_0757.py new file mode 100644 index 0000000000..938734a179 --- /dev/null +++ b/openedx/core/djangoapps/notifications/migrations/0004_auto_20230607_0757.py @@ -0,0 +1,63 @@ +# Generated by Django 3.2.19 on 2023-06-07 07:57 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone +import model_utils.fields +import opaque_keys.edx.django.models +import openedx.core.djangoapps.notifications.models + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('notifications', '0003_alter_notification_app_name'), + ] + + operations = [ + migrations.CreateModel( + name='CourseNotificationPreference', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')), + ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')), + ('course_id', opaque_keys.edx.django.models.CourseKeyField(max_length=255)), + ('notification_preference_config', models.JSONField(default=openedx.core.djangoapps.notifications.models.get_course_notification_preference_config)), + ('config_version', models.IntegerField(default=openedx.core.djangoapps.notifications.models.get_course_notification_preference_config_version)), + ('is_active', models.BooleanField(default=True)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notification_preferences', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'unique_together': {('user', 'course_id')}, + }, + ), + migrations.RemoveField( + model_name='notification', + name='content', + ), + migrations.AddField( + model_name='notification', + name='course_id', + field=opaque_keys.edx.django.models.CourseKeyField(blank=True, max_length=255, null=True), + ), + migrations.AlterField( + model_name='notification', + name='app_name', + field=models.CharField(db_index=True, max_length=64), + ), + migrations.AlterField( + model_name='notification', + name='content_context', + field=models.JSONField(default=dict), + ), + migrations.AlterField( + model_name='notification', + name='notification_type', + field=models.CharField(max_length=64), + ), + migrations.DeleteModel( + name='NotificationPreference', + ), + ] diff --git a/openedx/core/djangoapps/notifications/models.py b/openedx/core/djangoapps/notifications/models.py index 55c0bbde4a..1def57bab4 100644 --- a/openedx/core/djangoapps/notifications/models.py +++ b/openedx/core/djangoapps/notifications/models.py @@ -6,50 +6,67 @@ from django.db import models from model_utils.models import TimeStampedModel from opaque_keys.edx.django.models import CourseKeyField +from openedx.core.djangoapps.notifications.base_notification import NotificationAppManager User = get_user_model() NOTIFICATION_CHANNELS = ['web', 'push', 'email'] -# When notification preferences are updated, we need to update the CONFIG_VERSION. -NOTIFICATION_PREFERENCE_CONFIG = { - 'discussion': { - 'enabled': False, - 'notification_types': { - 'new_post': { - 'info': '', - 'web': False, - 'push': False, - 'email': False, - }, - 'core': { - 'info': '', - 'web': False, - 'push': False, - 'email': False, - }, - }, - # This is a list of notification channels for notification type that are not editable by the user. - # e.g. 'new_post' web notification is not editable by user i.e. 'not_editable': {'new_post': ['web']} - 'not_editable': {}, - }, -} -# Update this version when NOTIFICATION_PREFERENCE_CONFIG is updated. -NOTIFICATION_CONFIG_VERSION = 1 +# Update this version when there is a change to any course specific notification type or app. +COURSE_NOTIFICATION_CONFIG_VERSION = 1 -def get_notification_preference_config(): +def get_course_notification_preference_config(): """ - Returns the notification preference config. + Returns the course specific notification preference config. + + Sample Response: + { + 'discussion': { + 'enabled': True, + 'not_editable': { + 'new_comment_on_post': ['push'], + 'new_response_on_post': ['web'], + 'new_response_on_comment': ['web', 'push'] + }, + 'notification_types': { + 'new_comment_on_post': { + 'email': True, + 'push': True, + 'web': True, + 'info': 'Comment on post' + }, + 'new_response_on_comment': { + 'email': True, + 'push': True, + 'web': True, + 'info': 'Response on comment' + }, + 'new_response_on_post': { + 'email': True, + 'push': True, + 'web': True, + 'info': 'New Response on Post' + }, + 'core': { + 'email': True, + 'push': True, + 'web': True, + 'info': 'comment on post and response on comment' + } + }, + 'core_notification_types': [] + } + } """ - return NOTIFICATION_PREFERENCE_CONFIG + return NotificationAppManager().get_notification_app_preferences() -def get_notification_preference_config_version(): +def get_course_notification_preference_config_version(): """ Returns the notification preference config version. """ - return NOTIFICATION_CONFIG_VERSION + return COURSE_NOTIFICATION_CONFIG_VERSION def get_notification_channels(): @@ -59,27 +76,6 @@ def get_notification_channels(): return NOTIFICATION_CHANNELS -class NotificationApplication(models.TextChoices): - """ - Application choices where notifications are generated from - """ - DISCUSSION = 'DISCUSSION' - - -class NotificationType(models.TextChoices): - """ - Notification type choices - """ - NEW_CONTRIBUTION = 'NEW_CONTRIBUTION' - - -class NotificationTypeContent: - """ - Notification type content - """ - NEW_CONTRIBUTION_NOTIFICATION_CONTENT = 'There is a new contribution. {new_contribution}' - - class Notification(TimeStampedModel): """ Model to store notifications for users @@ -87,30 +83,33 @@ class Notification(TimeStampedModel): .. no_pii: """ user = models.ForeignKey(User, related_name="notifications", on_delete=models.CASCADE) - app_name = models.CharField(max_length=64, choices=NotificationApplication.choices, db_index=True) - notification_type = models.CharField(max_length=64, choices=NotificationType.choices) - content = models.CharField(max_length=1024) + course_id = CourseKeyField(max_length=255, null=True, blank=True) + app_name = models.CharField(max_length=64, db_index=True) + notification_type = models.CharField(max_length=64) content_context = models.JSONField(default=dict) content_url = models.URLField(null=True, blank=True) last_read = models.DateTimeField(null=True, blank=True) last_seen = models.DateTimeField(null=True, blank=True) def __str__(self): - return f'{self.user.username} - {self.app_name} - {self.notification_type} - {self.content}' + return f'{self.user.username} - {self.course_id} - {self.app_name} - {self.notification_type}' -class NotificationPreference(TimeStampedModel): +class CourseNotificationPreference(TimeStampedModel): """ Model to store notification preferences for users .. no_pii: """ user = models.ForeignKey(User, related_name="notification_preferences", on_delete=models.CASCADE) - course_id = CourseKeyField(max_length=255, blank=True, default=None) - notification_preference_config = models.JSONField(default=get_notification_preference_config) + course_id = CourseKeyField(max_length=255, null=False, blank=False) + notification_preference_config = models.JSONField(default=get_course_notification_preference_config) # This version indicates the current version of this notification preference. - config_version = models.IntegerField(blank=True, default=1) + config_version = models.IntegerField(default=get_course_notification_preference_config_version) is_active = models.BooleanField(default=True) + class Meta: + unique_together = ('user', 'course_id') + def __str__(self): return f'{self.user.username} - {self.course_id} - {self.notification_preference_config}' diff --git a/openedx/core/djangoapps/notifications/serializers.py b/openedx/core/djangoapps/notifications/serializers.py index 1285fdcb0e..a05c6bb62a 100644 --- a/openedx/core/djangoapps/notifications/serializers.py +++ b/openedx/core/djangoapps/notifications/serializers.py @@ -9,7 +9,7 @@ from openedx.core.djangoapps.content.course_overviews.models import CourseOvervi from openedx.core.djangoapps.notifications.models import ( get_notification_channels, Notification, - NotificationPreference, + CourseNotificationPreference, ) @@ -34,14 +34,14 @@ class NotificationCourseEnrollmentSerializer(serializers.ModelSerializer): fields = ('course',) -class UserNotificationPreferenceSerializer(serializers.ModelSerializer): +class UserCourseNotificationPreferenceSerializer(serializers.ModelSerializer): """ Serializer for user notification preferences. """ course_name = serializers.SerializerMethodField(read_only=True) class Meta: - model = NotificationPreference + model = CourseNotificationPreference fields = ('id', 'course_name', 'course_id', 'notification_preference_config',) read_only_fields = ('id', 'course_name', 'course_id',) write_only_fields = ('notification_preference_config',) @@ -136,7 +136,6 @@ class NotificationSerializer(serializers.ModelSerializer): 'id', 'app_name', 'notification_type', - 'content', 'content_context', 'content_url', 'last_read', diff --git a/openedx/core/djangoapps/notifications/tests/test_views.py b/openedx/core/djangoapps/notifications/tests/test_views.py index 2c8524e75f..5c5f440b38 100644 --- a/openedx/core/djangoapps/notifications/tests/test_views.py +++ b/openedx/core/djangoapps/notifications/tests/test_views.py @@ -14,8 +14,10 @@ from common.djangoapps.student.models import CourseEnrollment from common.djangoapps.student.tests.factories import UserFactory from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory from openedx.core.djangoapps.notifications.config.waffle import ENABLE_NOTIFICATIONS -from openedx.core.djangoapps.notifications.models import Notification, NotificationPreference, \ - get_notification_preference_config +from openedx.core.djangoapps.notifications.models import ( + Notification, + CourseNotificationPreference, +) from openedx.core.djangoapps.notifications.serializers import NotificationCourseEnrollmentSerializer from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory @@ -33,7 +35,6 @@ class CourseEnrollmentListViewTest(ModuleStoreTestCase): super().setUp() self.client = APIClient() self.user = UserFactory() - # self.client.force_authenticate(user=self.user) course_1 = CourseFactory.create( org='testorg', number='testcourse', @@ -123,8 +124,8 @@ class CourseEnrollmentPostSaveTest(ModuleStoreTestCase): created=True ) - # Assert that NotificationPreference object was created with correct attributes - notification_preferences = NotificationPreference.objects.all() + # Assert that CourseNotificationPreference object was created with correct attributes + notification_preferences = CourseNotificationPreference.objects.all() self.assertEqual(notification_preferences.count(), 1) self.assertEqual(notification_preferences[0].user, self.user) @@ -136,6 +137,7 @@ class UserNotificationPreferenceAPITest(ModuleStoreTestCase): """ Test for user notification preference API. """ + def setUp(self): super().setUp() self.user = UserFactory() @@ -169,7 +171,35 @@ class UserNotificationPreferenceAPITest(ModuleStoreTestCase): 'id': 1, 'course_name': 'course-v1:testorg+testcourse+testrun Course', 'course_id': 'course-v1:testorg+testcourse+testrun', - 'notification_preference_config': get_notification_preference_config(), + 'notification_preference_config': { + 'discussion': { + 'enabled': True, + 'core_notification_types': ['new_comment_on_response'], + 'notification_types': { + 'new_comment': { + 'web': True, + 'email': True, + 'push': True, + 'info': 'Comment on post' + }, + 'new_response': { + 'web': True, + 'email': True, + 'push': True, + 'info': 'Response on post' + }, + 'core': { + 'web': True, + 'email': True, + 'push': True, + 'info': '' + } + }, + 'non_editable': { + 'new_comment': ['web', 'email'] + } + } + } } def test_get_user_notification_preference_without_login(self): @@ -193,14 +223,14 @@ class UserNotificationPreferenceAPITest(ModuleStoreTestCase): ('discussion', None, None, False, status.HTTP_200_OK, 'app_update'), ('invalid_notification_app', None, None, True, status.HTTP_400_BAD_REQUEST, None), - ('discussion', 'new_post', 'web', True, status.HTTP_200_OK, 'type_update'), - ('discussion', 'new_post', 'web', False, status.HTTP_200_OK, 'type_update'), + ('discussion', 'new_comment', 'web', True, status.HTTP_200_OK, 'type_update'), + ('discussion', 'new_response', 'web', False, status.HTTP_200_OK, 'type_update'), ('discussion', 'core', 'email', True, status.HTTP_200_OK, 'type_update'), ('discussion', 'core', 'email', False, status.HTTP_200_OK, 'type_update'), ('discussion', 'invalid_notification_type', 'email', True, status.HTTP_400_BAD_REQUEST, None), - ('discussion', 'new_post', 'invalid_notification_channel', False, status.HTTP_400_BAD_REQUEST, None), + ('discussion', 'new_comment', 'invalid_notification_channel', False, status.HTTP_400_BAD_REQUEST, None), ) @ddt.unpack def test_patch_user_notification_preference( @@ -252,7 +282,6 @@ class NotificationListAPIViewTest(APITestCase): user=self.user, app_name='app1', notification_type='info', - content='This is a notification.', ) self.client.login(username=self.user.username, password='test') @@ -267,7 +296,6 @@ class NotificationListAPIViewTest(APITestCase): self.assertEqual(len(data), 1) self.assertEqual(data[0]['app_name'], 'app1') self.assertEqual(data[0]['notification_type'], 'info') - self.assertEqual(data[0]['content'], 'This is a notification.') def test_list_notifications_with_app_name_filter(self): """ @@ -278,13 +306,11 @@ class NotificationListAPIViewTest(APITestCase): user=self.user, app_name='app1', notification_type='info', - content='This is a notification for app1.', ) Notification.objects.create( user=self.user, app_name='app2', notification_type='info', - content='This is a notification for app2.', ) self.client.login(username=self.user.username, password='test') @@ -299,7 +325,6 @@ class NotificationListAPIViewTest(APITestCase): self.assertEqual(len(data), 1) self.assertEqual(data[0]['app_name'], 'app1') self.assertEqual(data[0]['notification_type'], 'info') - self.assertEqual(data[0]['content'], 'This is a notification for app1.') def test_list_notifications_without_authentication(self): """ @@ -363,6 +388,7 @@ class MarkNotificationsUnseenAPIViewTestCase(APITestCase): """ Tests for the MarkNotificationsUnseenAPIView. """ + def setUp(self): self.user = UserFactory() diff --git a/openedx/core/djangoapps/notifications/views.py b/openedx/core/djangoapps/notifications/views.py index b370aa7318..580f6609c9 100644 --- a/openedx/core/djangoapps/notifications/views.py +++ b/openedx/core/djangoapps/notifications/views.py @@ -12,15 +12,18 @@ from rest_framework.response import Response from rest_framework.views import APIView from common.djangoapps.student.models import CourseEnrollment -from openedx.core.djangoapps.notifications.models import NotificationPreference, \ - get_notification_preference_config_version +from openedx.core.djangoapps.notifications.models import ( + CourseNotificationPreference, + get_course_notification_preference_config_version +) from .config.waffle import ENABLE_NOTIFICATIONS from .models import Notification from .serializers import ( NotificationCourseEnrollmentSerializer, NotificationSerializer, - UserNotificationPreferenceSerializer, UserNotificationPreferenceUpdateSerializer + UserCourseNotificationPreferenceSerializer, + UserNotificationPreferenceUpdateSerializer ) User = get_user_model() @@ -142,12 +145,12 @@ class UserNotificationPreferenceView(APIView): } """ course_id = CourseKey.from_string(course_key_string) - user_notification_preference, _ = NotificationPreference.objects.get_or_create( + user_notification_preference, _ = CourseNotificationPreference.objects.get_or_create( user=request.user, course_id=course_id, is_active=True, ) - serializer = UserNotificationPreferenceSerializer(user_notification_preference) + serializer = UserCourseNotificationPreferenceSerializer(user_notification_preference) return Response(serializer.data) def patch(self, request, course_key_string): @@ -165,23 +168,23 @@ class UserNotificationPreferenceView(APIView): 400: Validation error """ course_id = CourseKey.from_string(course_key_string) - user_notification_preference = NotificationPreference.objects.get( + user_course_notification_preference = CourseNotificationPreference.objects.get( user=request.user, course_id=course_id, is_active=True, ) - if user_notification_preference.config_version != get_notification_preference_config_version(): + if user_course_notification_preference.config_version != get_course_notification_preference_config_version(): return Response( {'error': 'The notification preference config version is not up to date.'}, status=status.HTTP_409_CONFLICT, ) preference_update_serializer = UserNotificationPreferenceUpdateSerializer( - user_notification_preference, data=request.data, partial=True + user_course_notification_preference, data=request.data, partial=True ) preference_update_serializer.is_valid(raise_exception=True) updated_notification_preferences = preference_update_serializer.save() - serializer = UserNotificationPreferenceSerializer(updated_notification_preferences) + serializer = UserCourseNotificationPreferenceSerializer(updated_notification_preferences) return Response(serializer.data, status=status.HTTP_200_OK)