def test_purge_command_check(self):
        """
        Invoke the Management Command
        """
        msg_type = self.provider.save_notification_type(self.notification_type)
        msg1 = self.provider.save_notification_message(NotificationMessage(
            namespace='namespace1',
            msg_type=msg_type,
            payload={
                'foo': 'bar'
            }
        ))

        msg2 = self.provider.save_notification_message(NotificationMessage(
            namespace='namespace1',
            msg_type=msg_type,
            payload={
                'test': 'test'
            }
        ))

        # now reset the time to 66 days ago
        # in order to save the user notification message in the past.
        reset_time = datetime.now(pytz.UTC) - timedelta(days=66)
        with freeze_time(reset_time):
            self.provider.save_user_notification(UserNotification(
                user_id=self.test_user_id,
                msg=msg1
            ))

            self.provider.save_user_notification(UserNotification(
                user_id=self.test_user_id,
                msg=msg2
            ))

        # user notifications count
        self.assertEqual(
            self.provider.get_num_notifications_for_user(
                self.test_user_id,
                filters={
                    'namespace': 'namespace1'
                }
            ),
            2
        )
        # run the management command for purging notifications.
        force_purge.Command().handle()

        # now get the user notification count.
        # count should be 0 at that moment. because
        # all the notifications have been deleted.
        self.assertEqual(
            self.provider.get_num_notifications_for_user(
                self.test_user_id,
                filters={
                    'namespace': 'namespace1'
                }
            ),
            0
        )
Exemplo n.º 2
0
    def test_link_resolution(self):
        """
        Go through and set up a notification and publish it but with
        links to resolve
        """

        msg = NotificationMessage(namespace="test-runner", msg_type=self.msg_type, payload={"foo": "bar"})

        # this resolve_links resolutions are defined in settings.py
        # for testing purposes
        msg.add_click_link_params({"param1": "foo_param", "param2": "bar_param"})

        # make sure it asserts that user_id is an integer
        with self.assertRaises(ContractNotRespected):
            publish_notification_to_user("bad-id", msg)

        # now do happy path
        sent_user_msg = publish_notification_to_user(self.test_user_id, msg)

        # now make sure the links got resolved and put into
        # the payload
        self.assertIsNotNone(sent_user_msg.msg.get_click_link())

        # make sure the resolution is what we expect
        # NOTE: the mappings are defined in settings.py for testing purposes
        self.assertEqual(sent_user_msg.msg.get_click_link(), "/path/to/foo_param/url/bar_param")

        # now do it all over again since there is caching of link resolvers
        sent_user_msg = publish_notification_to_user(self.test_user_id, msg)
        self.assertTrue(sent_user_msg.msg.get_click_link())
        self.assertEqual(sent_user_msg.msg.get_click_link(), "/path/to/foo_param/url/bar_param")
Exemplo n.º 3
0
    def test_data_object_inequality(self):
        """
        Make sure that we can verify inequality between two objects
        """

        obj1 = DataObjectWithTypedFields(id=1,
                                         test_int_field=100,
                                         test_dict_field={'foo': 'bar'},
                                         test_class_field=NotificationMessage(
                                             msg_type=NotificationType(
                                                 name='testing',
                                                 renderer='foo.renderer',
                                             ),
                                             namespace='namespace',
                                             payload={'field': 'value'}))

        obj2 = DataObjectWithTypedFields(id=1,
                                         test_int_field=100,
                                         test_dict_field={'foo': 'bar'},
                                         test_class_field=NotificationMessage(
                                             msg_type=NotificationType(
                                                 name='something-different',
                                                 renderer='foo.renderer',
                                             ),
                                             namespace='namespace',
                                             payload={'field': 'value'}))

        self.assertNotEqual(obj1, obj2)
Exemplo n.º 4
0
    def setUp(self):
        """
        start up stuff
        """

        register_user_scope_resolver('list_scope', TestListScopeResolver())

        self.store = notification_store()
        self.msg_type = self.store.save_notification_type(
            NotificationType(
                name='foo.bar',
                renderer='foo',
            )
        )

        msg = NotificationMessage(
            msg_type=self.msg_type,
            payload={'foo': 'bar'},
        )
        msg.add_payload(
            {
                'extra': 'stuff'
            },
            channel_name='other_channel'
        )
        self.msg = self.store.save_notification_message(msg)
Exemplo n.º 5
0
    def test_multipayload(self):
        """
        Test that a channel will use the right payload
        """

        msg = NotificationMessage(namespace='test-runner',
                                  msg_type=self.msg_type,
                                  payload={'foo': 'bar'})

        msg.add_payload({'one': 'two'}, channel_name='durable')

        # now do happy path
        sent_user_msg = publish_notification_to_user(self.test_user_id, msg)

        # now query back the notification to make sure it got stored
        # and we can retrieve it
        self.assertEquals(get_notifications_count_for_user(self.test_user_id),
                          1)

        notifications = get_notifications_for_user(self.test_user_id)

        self.assertTrue(isinstance(notifications, list))
        self.assertEqual(len(notifications), 1)
        self.assertTrue(isinstance(notifications[0], UserNotification))

        read_user_msg = notifications[0]
        self.assertEqual(read_user_msg.user_id, self.test_user_id)
        self.assertIsNone(read_user_msg.read_at)  # should be unread

        self.assertEqual(read_user_msg, sent_user_msg)
        # make sure the message that got persisted contains only
        # the default payload
        self.assertEqual(read_user_msg.msg.payload,
                         msg.get_payload(channel_name='durable'))
Exemplo n.º 6
0
    def test_multipayload(self):
        """
        Test that a channel will use the right payload
        """

        msg = NotificationMessage(namespace="test-runner", msg_type=self.msg_type, payload={"foo": "bar"})

        msg.add_payload({"one": "two"}, channel_name="durable")

        # now do happy path
        sent_user_msg = publish_notification_to_user(self.test_user_id, msg)

        # now query back the notification to make sure it got stored
        # and we can retrieve it
        self.assertEquals(get_notifications_count_for_user(self.test_user_id), 1)

        notifications = get_notifications_for_user(self.test_user_id)

        self.assertTrue(isinstance(notifications, list))
        self.assertEqual(len(notifications), 1)
        self.assertTrue(isinstance(notifications[0], UserNotification))

        read_user_msg = notifications[0]
        self.assertEqual(read_user_msg.user_id, self.test_user_id)
        self.assertIsNone(read_user_msg.read_at)  # should be unread

        self.assertEqual(read_user_msg, sent_user_msg)
        # make sure the message that got persisted contains only
        # the default payload
        self.assertEqual(read_user_msg.msg.payload, msg.get_payload(channel_name="durable"))
Exemplo n.º 7
0
    def setUp(self):
        """
        start up stuff
        """

        register_user_scope_resolver('list_scope', TestListScopeResolver())

        self.store = notification_store()
        self.msg_type = self.store.save_notification_type(
            NotificationType(
                name='foo.bar',
                renderer='foo',
            )
        )

        msg = NotificationMessage(
            msg_type=self.msg_type,
            payload={'foo': 'bar'},
        )
        msg.add_payload(
            {
                'extra': 'stuff'
            },
            channel_name='other_channel'
        )
        self.msg = self.store.save_notification_message(msg)
    def fire_file_upload_notification(self, notifications_service):
        try:
            # this NotificationType is registered in the list of default Open edX Notifications
            msg_type = notifications_service.get_notification_type(
                'open-edx.xblock.group-project.file-uploaded')

            workgroup_user_ids = []
            uploader_username = ''
            for user in self.workgroup['users']:
                # don't send to ourselves
                if user['id'] != self.user_id:
                    workgroup_user_ids.append(user['id'])
                else:
                    uploader_username = user['username']

            # get the activity name which is simply our hosting
            # Sequence's Display Name, so call out to a new xBlock
            # runtime Service

            courseware_info = self.get_courseware_info(
                self.runtime.service(self, 'courseware_parent_info'))

            activity_name = courseware_info['activity_name']
            activity_location = courseware_info['activity_location']

            msg = NotificationMessage(msg_type=msg_type,
                                      namespace=unicode(self.course_id),
                                      payload={
                                          '_schema_version': 1,
                                          'action_username': uploader_username,
                                          'activity_name': activity_name,
                                      })

            #
            # add in all the context parameters we'll need to
            # generate a URL back to the website that will
            # present the new course announcement
            #
            # IMPORTANT: This can be changed to msg.add_click_link() if we
            # have a particular URL that we wish to use. In the initial use case,
            # we need to make the link point to a different front end website
            # so we need to resolve these links at dispatch time
            #
            msg.add_click_link_params({
                'course_id':
                unicode(self.course_id),
                'activity_location':
                unicode(activity_location) if activity_location else '',
            })

            # NOTE: We're not using Celery here since we are expectating that we
            # will have only a very small handful of workgroup users
            notifications_service.bulk_publish_notification_to_users(
                workgroup_user_ids, msg)
        except Exception, ex:
            # While we *should* send notification, if there is some
            # error here, we don't want to blow the whole thing up.
            # So log it and continue....
            log.exception(ex)
    def _set_activity_timed_notification(self, course_id, activity, msg_type,
                                         component, milestone_date,
                                         send_at_date, services,
                                         timer_name_suffix):

        component_name = component.name
        notifications_service = services.get('notifications')
        courseware_parent_info = services.get('courseware_parent_info')

        courseware_info = self.get_courseware_info(courseware_parent_info)

        activity_name = courseware_info['activity_name']
        activity_location = courseware_info['activity_location']

        project_location = courseware_info['project_location']

        milestone_date_tz = milestone_date.replace(tzinfo=pytz.UTC)
        send_at_date_tz = send_at_date.replace(tzinfo=pytz.UTC)

        msg = NotificationMessage(
            msg_type=notifications_service.get_notification_type(msg_type),
            namespace=unicode(course_id),
            payload={
                '_schema_version': 1,
                'activity_name': activity_name,
                'stage': component_name,
                'due_date': milestone_date_tz.strftime('%-m/%-d/%-y'),
            })

        #
        # add in all the context parameters we'll need to
        # generate a URL back to the website that will
        # present the new course announcement
        #
        # IMPORTANT: This can be changed to msg.add_click_link() if we
        # have a particular URL that we wish to use. In the initial use case,
        # we need to make the link point to a different front end website
        # so we need to resolve these links at dispatch time
        #
        msg.add_click_link_params({
            'course_id':
            unicode(course_id),
            'activity_location':
            unicode(activity_location),
        })

        notifications_service.publish_timed_notification(
            msg=msg,
            send_at=send_at_date_tz,
            # send to all students participating in this project
            scope_name='group_project_participants',
            scope_context={
                'course_id': unicode(course_id),
                'content_id': unicode(project_location),
            },
            timer_name=self._get_component_timer_name(component,
                                                      timer_name_suffix),
            ignore_if_past_due=True  # don't send if we're already late!
        )
Exemplo n.º 10
0
    def test_typed_fields(self):
        """
        Assert proper behavior with TypedFields inside of BaseDataObjects
        """

        # make sure we can make proper assignments on initialization
        msg = NotificationMessage()
        obj = DataObjectWithTypedFields(
            id=1,
            test_int_field=100,
            test_dict_field={
                'foo': 'bar'
            },
            test_class_field=msg,
        )

        self.assertTrue(isinstance(obj.test_int_field, int))
        self.assertTrue(isinstance(obj.test_dict_field, dict))
        self.assertTrue(isinstance(obj.test_class_field, NotificationMessage))

        self.assertEqual(obj.test_int_field, 100)
        self.assertEqual(obj.test_dict_field, {'foo': 'bar'})
        self.assertEqual(obj.test_class_field, msg)

        # make sure we work with longs as well
        obj = DataObjectWithTypedFields(
            id=long(1),
        )

        self.assertTrue(isinstance(obj.id, long))

        # make sure we can set fields after initialization

        obj = DataObjectWithTypedFields()
        obj.test_int_field = 100
        obj.test_dict_field = {
            'foo': 'bar'
        }
        obj.test_class_field = NotificationMessage()

        self.assertTrue(isinstance(obj.test_int_field, int))
        self.assertTrue(isinstance(obj.test_dict_field, dict))
        self.assertTrue(isinstance(obj.test_class_field, NotificationMessage))

        # make sure we can set typed fields as None
        obj = DataObjectWithTypedFields(
            test_int_field=None,
            test_dict_field=None,
            test_class_field=None,
        )

        self.assertTrue(isinstance(obj.test_int_field, type(None)))
        self.assertTrue(isinstance(obj.test_dict_field, type(None)))
        self.assertTrue(isinstance(obj.test_class_field, type(None)))

        with self.assertRaises(TypeError):
            # RelatedObjectField can only point to
            # subclasses of BaseDataObject
            RelatedObjectField(object)
    def fire_file_upload_notification(self, notifications_service):
        try:
            # this NotificationType is registered in the list of default Open edX Notifications
            msg_type = notifications_service.get_notification_type('open-edx.xblock.group-project.file-uploaded')

            workgroup_user_ids = []
            uploader_username = ''
            for user in self.workgroup['users']:
                # don't send to ourselves
                if user['id'] != self.user_id:
                    workgroup_user_ids.append(user['id'])
                else:
                    uploader_username = user['username']

            # get the activity name which is simply our hosting
            # Sequence's Display Name, so call out to a new xBlock
            # runtime Service

            courseware_info = self.get_courseware_info(self.runtime.service(self, 'courseware_parent_info'))

            activity_name = courseware_info['activity_name']
            activity_location = courseware_info['activity_location']

            msg = NotificationMessage(
                msg_type=msg_type,
                namespace=unicode(self.course_id),
                payload={
                    '_schema_version': 1,
                    'action_username': uploader_username,
                    'activity_name': activity_name,
                }
            )

            #
            # add in all the context parameters we'll need to
            # generate a URL back to the website that will
            # present the new course announcement
            #
            # IMPORTANT: This can be changed to msg.add_click_link() if we
            # have a particular URL that we wish to use. In the initial use case,
            # we need to make the link point to a different front end website
            # so we need to resolve these links at dispatch time
            #
            msg.add_click_link_params({
                'course_id': unicode(self.course_id),
                'activity_location': unicode(activity_location) if activity_location else '',
            })

            # NOTE: We're not using Celery here since we are expectating that we
            # will have only a very small handful of workgroup users
            notifications_service.bulk_publish_notification_to_users(
                workgroup_user_ids,
                msg
            )
        except Exception, ex:
            # While we *should* send notification, if there is some
            # error here, we don't want to blow the whole thing up.
            # So log it and continue....
            log.exception(ex)
Exemplo n.º 12
0
    def test_message_validation(self):
        """
        Make sure validation of NotificationMessage is correct
        """

        msg = NotificationMessage()  # intentionally blank

        with self.assertRaises(ValidationError):
            msg.validate()
Exemplo n.º 13
0
    def test_message_validation(self):
        """
        Make sure validation of NotificationMessage is correct
        """

        msg = NotificationMessage()  # intentionally blank

        with self.assertRaises(ValidationError):
            msg.validate()
def handle_progress_post_save_signal(sender, instance, **kwargs):
    """
    Handle the pre-save ORM event on CourseModuleCompletions
    """

    if settings.FEATURES['ENABLE_NOTIFICATIONS']:
        # If notifications feature is enabled, then we need to get the user's
        # rank before the save is made, so that we can compare it to
        # after the save and see if the position changes

        leaderboard_rank = StudentSocialEngagementScore.get_user_leaderboard_position(
            instance.course_id,
            user_id=instance.user.id,
            exclude_users=get_aggregate_exclusion_user_ids(instance.course_id)
        )['position']

        if leaderboard_rank == 0:
            # quick escape when user is not in the leaderboard
            # which means rank = 0. Trouble is 0 < 3, so unfortunately
            # the semantics around 0 don't match the logic below
            return

        # logic for Notification trigger is when a user enters into the Leaderboard
        leaderboard_size = getattr(settings, 'LEADERBOARD_SIZE', 3)
        presave_leaderboard_rank = instance.presave_leaderboard_rank if instance.presave_leaderboard_rank else sys.maxint
        if leaderboard_rank <= leaderboard_size and presave_leaderboard_rank > leaderboard_size:
            try:
                notification_msg = NotificationMessage(
                    msg_type=get_notification_type(u'open-edx.lms.leaderboard.engagement.rank-changed'),
                    namespace=unicode(instance.course_id),
                    payload={
                        '_schema_version': '1',
                        'rank': leaderboard_rank,
                        'leaderboard_name': 'Engagement',
                    }
                )

                #
                # add in all the context parameters we'll need to
                # generate a URL back to the website that will
                # present the new course announcement
                #
                # IMPORTANT: This can be changed to msg.add_click_link() if we
                # have a particular URL that we wish to use. In the initial use case,
                # we need to make the link point to a different front end website
                # so we need to resolve these links at dispatch time
                #
                notification_msg.add_click_link_params({
                    'course_id': unicode(instance.course_id),
                })

                publish_notification_to_user(int(instance.user.id), notification_msg)
            except Exception, ex:
                # Notifications are never critical, so we don't want to disrupt any
                # other logic processing. So log and continue.
                log.exception(ex)
def handle_progress_post_save_signal(sender, instance, **kwargs):
    """
    Handle the pre-save ORM event on CourseModuleCompletions
    """

    if settings.FEATURES['ENABLE_NOTIFICATIONS']:
        # If notifications feature is enabled, then we need to get the user's
        # rank before the save is made, so that we can compare it to
        # after the save and see if the position changes

        leaderboard_rank = StudentSocialEngagementScore.get_user_leaderboard_position(
            instance.course_id, instance.user.id,
            get_aggregate_exclusion_user_ids(instance.course_id))['position']

        if leaderboard_rank == 0:
            # quick escape when user is not in the leaderboard
            # which means rank = 0. Trouble is 0 < 3, so unfortunately
            # the semantics around 0 don't match the logic below
            return

        # logic for Notification trigger is when a user enters into the Leaderboard
        leaderboard_size = getattr(settings, 'LEADERBOARD_SIZE', 3)
        presave_leaderboard_rank = instance.presave_leaderboard_rank if instance.presave_leaderboard_rank else sys.maxint
        if leaderboard_rank <= leaderboard_size and presave_leaderboard_rank > leaderboard_size:
            try:
                notification_msg = NotificationMessage(
                    msg_type=get_notification_type(
                        u'open-edx.lms.leaderboard.engagement.rank-changed'),
                    namespace=unicode(instance.course_id),
                    payload={
                        '_schema_version': '1',
                        'rank': leaderboard_rank,
                        'leaderboard_name': 'Engagement',
                    })

                #
                # add in all the context parameters we'll need to
                # generate a URL back to the website that will
                # present the new course announcement
                #
                # IMPORTANT: This can be changed to msg.add_click_link() if we
                # have a particular URL that we wish to use. In the initial use case,
                # we need to make the link point to a different front end website
                # so we need to resolve these links at dispatch time
                #
                notification_msg.add_click_link_params({
                    'course_id':
                    unicode(instance.course_id),
                })

                publish_notification_to_user(int(instance.user.id),
                                             notification_msg)
            except Exception, ex:
                # Notifications are never critical, so we don't want to disrupt any
                # other logic processing. So log and continue.
                log.exception(ex)
    def _set_activity_timed_notification(self, course_id, activity, msg_type, component, milestone_date, send_at_date, services, timer_name_suffix):

        component_name = component.name
        notifications_service = services.get('notifications')
        courseware_parent_info = services.get('courseware_parent_info')

        courseware_info = self.get_courseware_info(courseware_parent_info)

        activity_name = courseware_info['activity_name']
        activity_location = courseware_info['activity_location']

        project_location = courseware_info['project_location']

        milestone_date_tz = milestone_date.replace(tzinfo=pytz.UTC)
        send_at_date_tz = send_at_date.replace(tzinfo=pytz.UTC)

        msg = NotificationMessage(
            msg_type=notifications_service.get_notification_type(msg_type),
            namespace=unicode(course_id),
            payload={
                '_schema_version': 1,
                'activity_name': activity_name,
                'stage': component_name,
                'due_date': milestone_date_tz.strftime('%-m/%-d/%-y'),
            }
        )

        #
        # add in all the context parameters we'll need to
        # generate a URL back to the website that will
        # present the new course announcement
        #
        # IMPORTANT: This can be changed to msg.add_click_link() if we
        # have a particular URL that we wish to use. In the initial use case,
        # we need to make the link point to a different front end website
        # so we need to resolve these links at dispatch time
        #
        msg.add_click_link_params({
            'course_id': unicode(course_id),
            'activity_location': unicode(activity_location),
        })

        notifications_service.publish_timed_notification(
            msg=msg,
            send_at=send_at_date_tz,
            # send to all students participating in this project
            scope_name='group_project_participants',
            scope_context={
                'course_id': unicode(course_id),
                'content_id': unicode(project_location),
            },
            timer_name=self._get_component_timer_name(component, timer_name_suffix),
            ignore_if_past_due=True  # don't send if we're already late!
        )
Exemplo n.º 17
0
def handle_studentgradebook_post_save_signal(sender, instance, **kwargs):
    """
    Handle the pre-save ORM event on CourseModuleCompletions
    """

    if settings.FEATURES['ENABLE_NOTIFICATIONS']:
        # attach the rank of the user before the save is completed
        data = StudentGradebook.get_user_position(
            instance.course_id,
            instance.user.id,
            exclude_users=get_aggregate_exclusion_user_ids(instance.course_id))

        leaderboard_rank = data['user_position']
        grade = data['user_grade']

        # logic for Notification trigger is when a user enters into the Leaderboard
        if grade > 0.0:
            leaderboard_size = getattr(settings, 'LEADERBOARD_SIZE', 3)
            presave_leaderboard_rank = instance.presave_leaderboard_rank if instance.presave_leaderboard_rank else sys.maxint
            if leaderboard_rank <= leaderboard_size and presave_leaderboard_rank > leaderboard_size:
                try:
                    notification_msg = NotificationMessage(
                        msg_type=get_notification_type(
                            u'open-edx.lms.leaderboard.gradebook.rank-changed'
                        ),
                        namespace=unicode(instance.course_id),
                        payload={
                            '_schema_version': '1',
                            'rank': leaderboard_rank,
                            'leaderboard_name': 'Proficiency',
                        })

                    #
                    # add in all the context parameters we'll need to
                    # generate a URL back to the website that will
                    # present the new course announcement
                    #
                    # IMPORTANT: This can be changed to msg.add_click_link() if we
                    # have a particular URL that we wish to use. In the initial use case,
                    # we need to make the link point to a different front end website
                    # so we need to resolve these links at dispatch time
                    #
                    notification_msg.add_click_link_params({
                        'course_id':
                        unicode(instance.course_id),
                    })

                    publish_notification_to_user(int(instance.user.id),
                                                 notification_msg)
                except Exception, ex:
                    # Notifications are never critical, so we don't want to disrupt any
                    # other logic processing. So log and continue.
                    log.exception(ex)
Exemplo n.º 18
0
    def test_purge_expired_read_notifications(self):
        """
        Test to check for the older read messages.
        If exists delete those messages from the database.
        """

        msg_type = self._save_notification_type()
        msg1 = self.provider.save_notification_message(
            NotificationMessage(namespace='namespace1',
                                msg_type=msg_type,
                                payload={'foo': 'bar'}))

        msg2 = self.provider.save_notification_message(
            NotificationMessage(namespace='namespace1',
                                msg_type=msg_type,
                                payload={'test': 'test'}))

        # now reset the time to 10 days ago
        # in order to save the user notification messages in the past.
        reset_time = datetime.now(pytz.UTC) - timedelta(days=10)
        with freeze_time(reset_time):
            self.provider.save_user_notification(
                UserNotification(user_id=self.test_user_id, msg=msg1))

            # mark the user notification as read.
            self.provider.mark_user_notifications_read(self.test_user_id)

        # now reset the time to 2 days ago
        # in order to save the user notification messages in the past.
        reset_time = datetime.now(pytz.UTC) - timedelta(days=2)
        with freeze_time(reset_time):
            self.provider.save_user_notification(
                UserNotification(user_id=self.test_user_id, msg=msg2))

            # mark the user notification as read.
            self.provider.mark_user_notifications_read(self.test_user_id)

        # user notifications count
        self.assertEqual(
            self.provider.get_num_notifications_for_user(
                self.test_user_id, filters={'namespace': 'namespace1'}), 2)

        # purge older read messages.
        purge_older_read_messages = datetime.now(pytz.UTC) - timedelta(days=6)
        self.provider.purge_expired_notifications(
            purge_read_messages_older_than=purge_older_read_messages)

        # now get the user notification count.
        # count should be 1 at that moment. because
        # only 1 notification has been deleted.
        self.assertEqual(
            self.provider.get_num_notifications_for_user(
                self.test_user_id, filters={'namespace': 'namespace1'}), 1)
Exemplo n.º 19
0
    def setUp(self):
        """
        Initialize tests, by creating users and populating some
        unread notifications
        """
        create_default_notification_preferences()
        self.store = notification_store()
        self.test_user_id = 1001
        self.from_timestamp = datetime.datetime.now(
            pytz.UTC) - datetime.timedelta(days=1)
        self.weekly_from_timestamp = datetime.datetime.now(
            pytz.UTC) - datetime.timedelta(days=7)
        self.to_timestamp = datetime.datetime.now(pytz.UTC)

        self.msg_type = self.store.save_notification_type(
            NotificationType(
                name='foo.bar',
                renderer=
                'edx_notifications.renderers.basic.BasicSubjectBodyRenderer',
            ))

        self.msg_type_no_renderer = self.store.save_notification_type(
            NotificationType(
                name='foo.baz',
                renderer='foo',
            ))

        # create two notifications
        with freeze_time(self.to_timestamp):
            msg = self.store.save_notification_message(
                NotificationMessage(
                    msg_type=self.msg_type,
                    namespace='foo',
                    payload={
                        'subject': 'foo',
                        'body': 'bar'
                    },
                ))
            self.notification1 = publish_notification_to_user(
                self.test_user_id, msg)

        with freeze_time(self.to_timestamp):
            msg = self.store.save_notification_message(
                NotificationMessage(
                    msg_type=self.msg_type_no_renderer,
                    namespace='bar',
                    payload={
                        'subject': 'foo',
                        'body': 'bar'
                    },
                ))
            self.notification2 = publish_notification_to_user(
                self.test_user_id, msg)
def handle_studentgradebook_post_save_signal(sender, instance, **kwargs):
    """
    Handle the pre-save ORM event on CourseModuleCompletions
    """
    invalid_user_data_cache('grade', instance.course_id, instance.user.id)

    if settings.FEATURES['ENABLE_NOTIFICATIONS']:
        # attach the rank of the user before the save is completed
        data = StudentGradebook.get_user_position(
            instance.course_id,
            user_id=instance.user.id,
            exclude_users=get_aggregate_exclusion_user_ids(instance.course_id)
        )

        leaderboard_rank = data['user_position']
        grade = data['user_grade']

        # logic for Notification trigger is when a user enters into the Leaderboard
        if grade > 0.0:
            leaderboard_size = getattr(settings, 'LEADERBOARD_SIZE', 3)
            presave_leaderboard_rank = instance.presave_leaderboard_rank if instance.presave_leaderboard_rank else sys.maxint
            if leaderboard_rank <= leaderboard_size and presave_leaderboard_rank > leaderboard_size:
                try:
                    notification_msg = NotificationMessage(
                        msg_type=get_notification_type(u'open-edx.lms.leaderboard.gradebook.rank-changed'),
                        namespace=unicode(instance.course_id),
                        payload={
                            '_schema_version': '1',
                            'rank': leaderboard_rank,
                            'leaderboard_name': 'Proficiency',
                        }
                    )

                    #
                    # add in all the context parameters we'll need to
                    # generate a URL back to the website that will
                    # present the new course announcement
                    #
                    # IMPORTANT: This can be changed to msg.add_click_link() if we
                    # have a particular URL that we wish to use. In the initial use case,
                    # we need to make the link point to a different front end website
                    # so we need to resolve these links at dispatch time
                    #
                    notification_msg.add_click_link_params({
                        'course_id': unicode(instance.course_id),
                    })

                    publish_notification_to_user(int(instance.user.id), notification_msg)
                except Exception, ex:
                    # Notifications are never critical, so we don't want to disrupt any
                    # other logic processing. So log and continue.
                    log.exception(ex)
Exemplo n.º 21
0
    def fire_grades_posted_notification(self, group_id, notifications_service):
        try:
            # this NotificationType is registered in the list of default Open edX Notifications
            msg_type = notifications_service.get_notification_type(
                'open-edx.xblock.group-project.grades-posted')

            # get the activity name which is simply our hosting
            # Sequence's Display Name, so call out to a new xBlock
            # runtime Service
            courseware_info = self.get_courseware_info(
                self.runtime.service(self, 'courseware_parent_info'))
            activity_name = courseware_info['activity_name']
            activity_location = courseware_info['activity_location']

            msg = NotificationMessage(msg_type=msg_type,
                                      namespace=unicode(self.course_id),
                                      payload={
                                          '_schema_version': 1,
                                          'activity_name': activity_name,
                                      })

            #
            # add in all the context parameters we'll need to
            # generate a URL back to the website that will
            # present the new course announcement
            #
            # IMPORTANT: This can be changed to msg.add_click_link() if we
            # have a particular URL that we wish to use. In the initial use case,
            # we need to make the link point to a different front end website
            # so we need to resolve these links at dispatch time
            #
            msg.add_click_link_params({
                'course_id':
                unicode(self.course_id),
                'activity_location':
                unicode(activity_location) if activity_location else '',
            })

            # Bulk publish to the 'group_project_workgroup' user scope
            notifications_service.bulk_publish_notification_to_scope(
                'group_project_workgroup',
                {
                    # I think self.workgroup['id'] is a string version of an integer
                    'workgroup_id': group_id,
                },
                msg)
        except Exception, ex:
            # While we *should* send notification, if there is some
            # error here, we don't want to blow the whole thing up.
            # So log it and continue....
            log.exception(ex)
Exemplo n.º 22
0
    def _setup_user_notifications(self):
        """
        Helper to build out some
        """

        msg_type = self._save_notification_type()

        # set up some notifications

        msg1 = self.provider.save_notification_message(NotificationMessage(
            namespace='namespace1',
            msg_type=msg_type,
            payload={
                'foo': 'bar',
                'one': 1,
                'none': None,
                'datetime': datetime.utcnow(),
                'iso8601-fakeout': '--T::',  # something to throw off the iso8601 parser heuristic
            }
        ))

        map1 = self.provider.save_user_notification(UserNotification(
            user_id=self.test_user_id,
            msg=msg1
        ))

        msg_type2 = self.provider.save_notification_type(
            NotificationType(
                name='foo.bar.another',
                renderer='foo.renderer',
            )
        )

        msg2 = self.provider.save_notification_message(NotificationMessage(
            namespace='namespace2',
            msg_type=msg_type2,
            payload={
                'foo': 'baz',
                'one': 1,
                'none': None,
                'datetime': datetime.utcnow(),
                'iso8601-fakeout': '--T::',  # something to throw off the iso8601 parser heuristic
            }
        ))

        map2 = self.provider.save_user_notification(UserNotification(
            user_id=self.test_user_id,
            msg=msg2
        ))

        return map1, msg1, map2, msg2
    def fire_grades_posted_notification(self, group_id, notifications_service):
        try:
            # this NotificationType is registered in the list of default Open edX Notifications
            msg_type = notifications_service.get_notification_type('open-edx.xblock.group-project.grades-posted')

            # get the activity name which is simply our hosting
            # Sequence's Display Name, so call out to a new xBlock
            # runtime Service
            courseware_info = self.get_courseware_info(self.runtime.service(self, 'courseware_parent_info'))
            activity_name = courseware_info['activity_name']
            activity_location = courseware_info['activity_location']

            msg = NotificationMessage(
                msg_type=msg_type,
                namespace=unicode(self.course_id),
                payload={
                    '_schema_version': 1,
                    'activity_name': activity_name,
                }
            )

            #
            # add in all the context parameters we'll need to
            # generate a URL back to the website that will
            # present the new course announcement
            #
            # IMPORTANT: This can be changed to msg.add_click_link() if we
            # have a particular URL that we wish to use. In the initial use case,
            # we need to make the link point to a different front end website
            # so we need to resolve these links at dispatch time
            #
            msg.add_click_link_params({
                'course_id': unicode(self.course_id),
                'activity_location': unicode(activity_location) if activity_location else '',
            })

            # Bulk publish to the 'group_project_workgroup' user scope
            notifications_service.bulk_publish_notification_to_scope(
                'group_project_workgroup',
                {
                    # I think self.workgroup['id'] is a string version of an integer
                    'workgroup_id': group_id,
                },
                msg
            )
        except Exception, ex:
            # While we *should* send notification, if there is some
            # error here, we don't want to blow the whole thing up.
            # So log it and continue....
            log.exception(ex)
Exemplo n.º 24
0
    def test_multiple_notifications(self):
        """
        Test Case for retrieving multiple notifications
        """

        msg1 = NotificationMessage(
            namespace='test-runner',
            msg_type=self.msg_type,
            payload={
                'foo': 'bar'
            }
        )

        msg2 = NotificationMessage(
            namespace='test-runner',
            msg_type=self.msg_type,
            payload={
                'second': 'one'
            }
        )

        # publish
        user_msg1 = publish_notification_to_user(self.user.id, msg1)
        user_msg2 = publish_notification_to_user(self.user.id, msg2)

        response = self.client.get(reverse('edx_notifications.consumer.notifications'))
        self.assertEqual(response.status_code, 200)

        results = json.loads(response.content.decode('utf-8'))
        self.assertEqual(len(results), 2)

        # the last one written should be the first one read
        self._compare_user_msg_to_result(user_msg2, results[0])
        # the first one written should be second one received
        self._compare_user_msg_to_result(user_msg1, results[1])

        # now do query with a namespace filter
        response = self.client.get(
            reverse('edx_notifications.consumer.notifications'),
            {
                'namespace': 'test-runner'
            }
        )
        self.assertEqual(response.status_code, 200)

        results = json.loads(response.content.decode('utf-8'))
        self.assertEqual(len(results), 2)  # did we get two back?

        self._compare_user_msg_to_result(user_msg2, results[0])
        self._compare_user_msg_to_result(user_msg1, results[1])
Exemplo n.º 25
0
    def test_click_links_params(self):
        """
        Make sure the helper methods work
        """

        msg = NotificationMessage(payload={'foo': 'bar'})

        msg.add_click_link_params({
            'param1': 'val1',
            'param2': 'val2',
        })

        click_links = msg.get_click_link_params()

        self.assertIsNotNone(click_links)
        self.assertEqual(click_links['param1'], 'val1')
        self.assertEqual(click_links['param2'], 'val2')

        msg.add_click_link_params({
            'param3': 'val3',
        })

        click_links = msg.get_click_link_params()

        self.assertEqual(click_links['param1'], 'val1')
        self.assertEqual(click_links['param2'], 'val2')
        self.assertEqual(click_links['param3'], 'val3')
Exemplo n.º 26
0
    def test_over_limit_counting(self):
        """
        Verifies that our counting operations will work as expected even when
        our count is greater that the NOTIFICATION_MAX_LIST_SIZE which is
        the maximum page size
        """

        self.assertEqual(const.NOTIFICATION_MAX_LIST_SIZE, 1)

        msg_type = self._save_notification_type()

        for __ in range(10):
            msg = self.provider.save_notification_message(NotificationMessage(
                namespace='namespace1',
                msg_type=msg_type,
                payload={
                    'foo': 'bar'
                }
            ))

            self.provider.save_user_notification(UserNotification(
                user_id=self.test_user_id,
                msg=msg
            ))

        self.assertEqual(
            self.provider.get_num_notifications_for_user(
                self.test_user_id,
                filters={
                    'namespace': 'namespace1',
                }
            ),
            10
        )
Exemplo n.º 27
0
    def test_wildcard_group_mapping(self):
        """
        Test that adds the default notification type mapping
        """
        msg_type = self.store.save_notification_type(
            NotificationType(
                name='open-edx.lms.discussions.new-discussion-added',
                renderer='open-edx.lms.discussions.new-discussion-added',
            )
        )
        # create cohort notification
        msg = self.store.save_notification_message(
            NotificationMessage(
                msg_type=msg_type,
                namespace='cohort-thread-added',
                payload={'subject': 'foo', 'body': 'bar'},
            )
        )
        publish_notification_to_user(self.test_user_id, msg)

        register_namespace_resolver(TestNamespaceResolver())

        set_user_notification_preference(self.test_user_id, const.NOTIFICATION_DAILY_DIGEST_PREFERENCE_NAME, 'true')

        self.assertEqual(
            send_notifications_digest(
                self.from_timestamp,
                self.to_timestamp,
                const.NOTIFICATION_DAILY_DIGEST_PREFERENCE_NAME,
                'subject',
                '*****@*****.**'
            ),
            3
        )
Exemplo n.º 28
0
    def _save_new_notification(self, payload='This is a test payload'):
        """
        Helper method to create a new notification
        """

        msg_type = self._save_notification_type()

        msg = NotificationMessage(
            msg_type=msg_type,
            payload={
                'foo': 'bar',
                'one': 1,
                'none': None,
                'datetime': datetime.utcnow(),
                'iso8601-fakeout': '--T::',  # something to throw off the iso8601 parser heuristic
            },
            resolve_links={
                'param1': 'value1'
            },
            object_id='foo-item'
        )

        with self.assertNumQueries(1):
            result = self.provider.save_notification_message(msg)

        self.assertIsNotNone(result)
        self.assertIsNotNone(result.id)
        return result
Exemplo n.º 29
0
    def test_mark_user_notification_read(self):
        """

        """
        msg_type = self._save_notification_type()
        for __ in range(10):
            msg = self.provider.save_notification_message(
                NotificationMessage(namespace='namespace1',
                                    msg_type=msg_type,
                                    payload={'foo': 'bar'}))

            self.provider.save_user_notification(
                UserNotification(user_id=self.test_user_id, msg=msg))

        self.assertEqual(
            self.provider.get_num_notifications_for_user(self.test_user_id,
                                                         filters={
                                                             'namespace':
                                                             'namespace1',
                                                         }), 10)
        self.provider.mark_user_notifications_read(self.test_user_id)

        self.assertEqual(
            self.provider.get_num_notifications_for_user(self.test_user_id,
                                                         filters={
                                                             'namespace':
                                                             'namespace1',
                                                             'read': False
                                                         }), 0)
Exemplo n.º 30
0
    def test_bulk_publish_list_exclude(self):
        """
        Make sure we can bulk publish to a number of users
        passing in a list, and also pass in an exclusion list to
        make sure the people in the exclude list does not get
        the notification
        """

        msg = NotificationMessage(namespace='test-runner',
                                  msg_type=self.msg_type,
                                  payload={'foo': 'bar'})

        user_ids = list(
            range(1, const.NOTIFICATION_BULK_PUBLISH_CHUNK_SIZE * 2 + 1))
        exclude_user_ids = list(
            range(1, const.NOTIFICATION_BULK_PUBLISH_CHUNK_SIZE * 2 + 1, 2))

        # now send to more than our internal chunking size
        bulk_publish_notification_to_users(user_ids,
                                           msg,
                                           exclude_user_ids=exclude_user_ids)

        # now read them all back
        for user_id in range(
                1, const.NOTIFICATION_BULK_PUBLISH_CHUNK_SIZE * 2 + 1):
            notifications = get_notifications_for_user(user_id)

            self.assertTrue(isinstance(notifications, list))
            self.assertEqual(len(notifications),
                             1 if user_id not in exclude_user_ids else 0)
            if user_id not in exclude_user_ids:
                self.assertTrue(isinstance(notifications[0], UserNotification))
Exemplo n.º 31
0
    def _set_activity_timed_notification(self, course_id, msg_type, event_date,
                                         send_at_date, services,
                                         timer_name_suffix):

        notifications_service = services.get('notifications')

        activity_date_tz = event_date.replace(tzinfo=pytz.UTC)
        send_at_date_tz = send_at_date.replace(tzinfo=pytz.UTC)

        msg = NotificationMessage(
            msg_type=notifications_service.get_notification_type(msg_type),
            namespace=unicode(course_id),
            payload={
                '_schema_version': 1,
                'activity_name': self.activity.display_name,
                'stage': self.display_name,
                'due_date': activity_date_tz.strftime('%-m/%-d/%-y'),
            })

        add_click_link_params(msg, course_id, self.location)

        notifications_service.publish_timed_notification(
            msg=msg,
            send_at=send_at_date_tz,
            # send to all students participating in this project
            scope_name=NotificationScopes.PARTICIPANTS,
            scope_context={
                'course_id': unicode(course_id),
                'content_id': unicode(self.activity.project.location),
            },
            timer_name=self._get_stage_timer_name(timer_name_suffix),
            ignore_if_past_due=True  # don't send if we're already late!
        )
Exemplo n.º 32
0
    def test_publish_to_scope(self):
        """
        Make sure we can bulk publish to a number of users
        passing in a resultset from a Django ORM query
        """

        register_user_scope_resolver("list_scope", TestListScopeResolver())

        msg = NotificationMessage(namespace='test-runner',
                                  msg_type=self.msg_type,
                                  payload={'foo': 'bar'})

        bulk_publish_notification_to_scope(
            scope_name="list_scope",
            # the TestListScopeResolver expects a "range" property in the context
            scope_context={"range": 5},
            msg=msg)

        for user_id in range(4):
            # have to fudge this a bit as the contract on user_id
            # says > 0 only allowed
            user_id = user_id + 1
            notifications = get_notifications_for_user(user_id)

            self.assertTrue(isinstance(notifications, list))
            self.assertEqual(len(notifications), 1)
            self.assertTrue(isinstance(notifications[0], UserNotification))
Exemplo n.º 33
0
    def test_json_renderer(self):
        """
        Make sure JSON renderer returns correct renderings
        """

        msg_type = NotificationType(
            name='open-edx.edx_notifications.lib.tests.test_publisher',
            renderer='edx_notifications.renderers.basic.JsonRenderer',
        )
        register_notification_type(msg_type)

        msg = NotificationMessage(namespace='test-runner',
                                  msg_type=msg_type,
                                  payload={
                                      'subject': 'test subject',
                                      'body': 'test body',
                                  })

        renderer = JsonRenderer()

        self.assertTrue(renderer.can_render_format(RENDER_FORMAT_JSON))
        self.assertIsNone(renderer.get_template_path(RENDER_FORMAT_JSON))
        self.assertEqual(
            json.loads(renderer.render(msg, RENDER_FORMAT_JSON, None)),
            msg.payload)
Exemplo n.º 34
0
    def test_bulk_publish_generator(self):
        """
        Make sure we can bulk publish to a number of users
        passing in a generator function
        """

        msg = NotificationMessage(namespace='test-runner',
                                  msg_type=self.msg_type,
                                  payload={'foo': 'bar'})

        def _user_id_generator():
            """
            Just spit our an generator that goes from 1 to 100
            """
            for user_id in range(1, 100):
                yield user_id

        # now send to more than our internal chunking size
        bulk_publish_notification_to_users(_user_id_generator(), msg)

        # now read them all back
        for user_id in range(1, 100):
            notifications = get_notifications_for_user(user_id)

            self.assertTrue(isinstance(notifications, list))
            self.assertEqual(len(notifications), 1)
            self.assertTrue(isinstance(notifications[0], UserNotification))
Exemplo n.º 35
0
def _create_notification_message(app_id, payload):
    notification_type = get_notification_type(
        'open-edx.mobileapps.notifications')
    notification_message = NotificationMessage(namespace=str(app_id),
                                               msg_type=notification_type,
                                               payload=payload)
    return notification_message
Exemplo n.º 36
0
    def test_bulk_publish_list(self):
        """
        Make sure we can bulk publish to a number of users
        passing in a list
        """

        msg = NotificationMessage(
            namespace='test-runner',
            msg_type=self.msg_type,
            payload={
                'foo': 'bar'
            }
        )

        # now send to more than our internal chunking size
        bulk_publish_notification_to_users(
            list(range(1, const.NOTIFICATION_BULK_PUBLISH_CHUNK_SIZE * 2 + 1)),
            msg
        )

        # now read them all back
        for user_id in range(1, const.NOTIFICATION_BULK_PUBLISH_CHUNK_SIZE * 2 + 1):
            notifications = get_notifications_for_user(user_id)

            self.assertTrue(isinstance(notifications, list))
            self.assertEqual(len(notifications), 1)
            self.assertTrue(isinstance(notifications[0], UserNotification))
Exemplo n.º 37
0
    def test_bulk_publish_orm_query(self):
        """
        Make sure we can bulk publish to a number of users
        passing in a resultset from a Django ORM query
        """

        # set up some test users in Django User's model
        User(username='******').save()
        User(username='******').save()
        User(username='******').save()

        msg = NotificationMessage(namespace='test-runner',
                                  msg_type=self.msg_type,
                                  payload={'foo': 'bar'})

        resultset = User.objects.values_list('id', flat=True).all()  # pylint: disable=no-member

        num_sent = bulk_publish_notification_to_users(resultset, msg)

        # make sure we sent 3
        self.assertEqual(num_sent, 3)

        # now read them back
        for user in User.objects.all():  # pylint: disable=no-member
            notifications = get_notifications_for_user(user.id)

            self.assertTrue(isinstance(notifications, list))
            self.assertEqual(len(notifications), 1)
            self.assertTrue(isinstance(notifications[0], UserNotification))
Exemplo n.º 38
0
    def test_someone_elses_notification(self):
        """
        Simple test to make sure that we can get counts for someone elses
        notification
        """

        msg = NotificationMessage(
            namespace='test-runner',
            msg_type=self.msg_type,
            payload={
                'foo': 'bar'
            }
        )

        # publish to some other user_id
        user_msg = publish_notification_to_user(99999, msg)
        self.assertIsNotNone(user_msg)

        # now query API
        response = self.client.get(reverse('edx_notifications.consumer.notifications.count'))
        self.assertEqual(response.status_code, 200)

        results = json.loads(response.content.decode('utf-8'))
        self.assertIn('count', results)
        self.assertEqual(results['count'], 0)
Exemplo n.º 39
0
    def fire_file_upload_notification(self, notifications_service):

        log.info('{}.fire_file_upload_notification on location = {}'.format(
            self.__class__.__name__, self.location))

        # this NotificationType is registered in the list of default Open edX Notifications
        msg_type = notifications_service.get_notification_type(
            NotificationMessageTypes.FILE_UPLOADED)

        workgroup_user_ids = []
        uploader_username = ''
        for user in self.workgroup.users:
            # don't send to ourselves
            if user.id != self.user_id:
                workgroup_user_ids.append(user.id)
            else:
                uploader_username = user.username

        msg = NotificationMessage(msg_type=msg_type,
                                  namespace=unicode(self.course_id),
                                  payload={
                                      '_schema_version': 1,
                                      'action_username': uploader_username,
                                      'activity_name':
                                      self.activity.display_name,
                                  })
        location = unicode(self.location) if self.location else ''
        add_click_link_params(msg, unicode(self.course_id), location)

        # NOTE: We're not using Celery here since we are expecting that we
        # will have only a very small handful of workgroup users
        notifications_service.bulk_publish_notification_to_users(
            workgroup_user_ids, msg)
Exemplo n.º 40
0
    def test_click_links_params(self):
        """
        Make sure the helper methods work
        """

        msg = NotificationMessage(
            payload={'foo': 'bar'}
        )

        msg.add_click_link_params({
            'param1': 'val1',
            'param2': 'val2',
        })

        click_links = msg.get_click_link_params()

        self.assertIsNotNone(click_links)
        self.assertEqual(click_links['param1'], 'val1')
        self.assertEqual(click_links['param2'], 'val2')

        msg.add_click_link_params({
            'param3': 'val3',
        })

        click_links = msg.get_click_link_params()

        self.assertEqual(click_links['param1'], 'val1')
        self.assertEqual(click_links['param2'], 'val2')
        self.assertEqual(click_links['param3'], 'val3')
Exemplo n.º 41
0
    def test_click_link(self):
        """
        Tests around the click_link property of NotificationMessages
        """

        msg = NotificationMessage()

        self.assertIsNone(msg.get_click_link())

        msg.set_click_link('/foo/bar/baz')
        self.assertEqual(msg.get_click_link(), '/foo/bar/baz')

        msg.set_click_link('/updated')
        self.assertEqual(msg.get_click_link(), '/updated')
Exemplo n.º 42
0
    def test_cloning(self):
        """
        Make sure cloning works
        """

        msg = NotificationMessage(
            payload={'foo': 'bar'}
        )

        clone = NotificationMessage.clone(msg)

        self.assertEqual(msg, clone)

        # now change the cloned payload and assert that the original one
        # did not change

        clone.payload['foo'] = 'changed'
        self.assertEqual(msg.payload['foo'], 'bar')
        self.assertEqual(clone.payload['foo'], 'changed')
Exemplo n.º 43
0
    def _get_linked_resolved_msg(self, msg):
        """
        This helper will attempt to resolve all
        links that are present in the message

        resolve any links that may need conversion into URL paths
        This uses a subdict named "_resolve_links" in the msg.resolve_links
        field:

            resolve_links = {
                "_resolve_links": {
                    "_click_link": {
                       "param1": "val1",
                       "param2": "param2"
                    },
                    :
                },
             :
            }

        This above will try to resolve the URL for the link named "_click_link" (for
        example, when a user clicks on a notification, the should go to that URL), with the
        URL parameters "param1"="val1" and "param2"="val2", and put that link name back in
        the main payload dictionary as "_click_link"
        """

        if msg.resolve_links:
            for link_name, link_params in msg.resolve_links.iteritems():
                resolved_link = self.resolve_msg_link(msg, link_name,
                                                      link_params)
                if resolved_link:
                    # copy the msg because we are going to alter it and we don't want to affect
                    # the passed in version
                    msg = NotificationMessage.clone(msg)

                    # if we could resolve, then store the resolved link in the payload itself
                    msg.payload[link_name] = resolved_link

        # return the msg which could be a clone of the original one
        return msg
Exemplo n.º 44
0
    def test_multi_payloads(self):
        """
        Tests the ability to support multiple payloads in a NotificationMessage
        """

        msg = NotificationMessage()
        self.assertIsNone(msg.get_payload())

        msg.add_payload(
            {
                'foo': 'bar',
            }
        )

        self.assertEqual(msg.get_payload(), {'foo': 'bar'})
        self.assertEqual(msg.get_message_for_channel(), msg)

        msg.add_payload(
            {
                'bar': 'baz'
            },
            channel_name='channel1'
        )

        self.assertNotEqual(msg.get_message_for_channel(), msg)
        self.assertEqual(msg.get_message_for_channel().payload, {'foo': 'bar'})
        self.assertEqual(msg.get_message_for_channel('channel1').payload, {'bar': 'baz'})

        msg.add_payload(
            {
                'one': 'two'
            },
            channel_name='channel2'
        )

        self.assertNotEqual(msg.get_message_for_channel(), msg)
        self.assertEqual(msg.get_message_for_channel().payload, {'foo': 'bar'})
        self.assertEqual(msg.get_message_for_channel('channel1').payload, {'bar': 'baz'})
        self.assertEqual(msg.get_message_for_channel('channel2').payload, {'one': 'two'})
        self.assertEqual(msg.get_message_for_channel('doesnt-exist').payload, {'foo': 'bar'})

        msg.add_payload(
            {
                'updated': 'yes'
            }
        )
        self.assertEqual(msg.get_message_for_channel().payload, {'updated': 'yes'})
Exemplo n.º 45
0
def index(request):
    """
    Returns a basic HTML snippet rendering of a notification count
    """
    global NAMESPACE

    if request.method == 'POST':

        register_user_scope_resolver('user_email_resolver', TestUserResolver(request.user))

        if request.POST.get('change_namespace'):
            namespace_str = request.POST['namespace']
            NAMESPACE = namespace_str if namespace_str != "None" else None
        elif request.POST.get('send_digest'):
            send_digest(request, request.POST.get('digest_email'))
        else:
            type_name = request.POST['notification_type']
            channel_name = request.POST['notification_channel']
            if not channel_name:
                channel_name = None
            msg_type = get_notification_type(type_name)

            msg = NotificationMessage(
                msg_type=msg_type,
                namespace=NAMESPACE,
                payload=CANNED_TEST_PAYLOAD[type_name],
            )

            if type_name == 'testserver.msg-with-resolved-click-link':
                msg.add_click_link_params({
                    'param1': 'param_val1',
                    'param2': 'param_val2',
                })

            publish_notification_to_user(request.user.id, msg, preferred_channel=channel_name)

    template = loader.get_template('index.html')


    # call to the helper method to build up all the context we need
    # to render the "notification_widget" that is embedded in our
    # test page
    context_dict = get_notifications_widget_context({
        'user': request.user,
        'notification_types': get_all_notification_types(),
        'global_variables': {
            'app_name': 'Notification Test Server',
            'hide_link_is_visible': settings.HIDE_LINK_IS_VISIBLE,
            'always_show_dates_on_unread': True,
            'notification_preference_tab_is_visible': settings.NOTIFICATION_PREFERENCES_IS_VISIBLE,
        },
        # for test purposes, set up a short-poll which contacts the server
        # every 10 seconds to see if there is a new notification
        #
        # NOTE: short-poll technique should not be used in a production setting with
        # any reasonable number of concurrent users. This is just for
        # testing purposes.
        #
        'refresh_watcher': {
            'name': 'short-poll',
            'args': {
                'poll_period_secs': 10,
            },
        },
        'include_framework_js': True,
        'namespace': NAMESPACE,
    })

    return HttpResponse(template.render(RequestContext(request, context_dict)))
Exemplo n.º 46
0
def _send_discussion_notification(
    type_name,
    course_id,
    thread,
    request_user,
    excerpt=None,
    recipient_user_id=None,
    recipient_group_id=None,
    recipient_exclude_user_ids=None,
    extra_payload=None,
    is_anonymous_user=False
):
    """
    Helper method to consolidate Notification trigger workflow
    """
    try:
        # is Notifications feature enabled?
        if not settings.FEATURES.get("ENABLE_NOTIFICATIONS", False):
            return

        if is_anonymous_user:
            action_username = _('An anonymous user')
        else:
            action_username = request_user.username

        # get the notification type.
        msg = NotificationMessage(
            msg_type=get_notification_type(type_name),
            namespace=course_id,
            # base payload, other values will be passed in as extra_payload
            payload={
                '_schema_version': '1',
                'action_username': action_username,
                'thread_title': thread.title,
            }
        )

        # add in additional payload info
        # that might be type specific
        if extra_payload:
            msg.payload.update(extra_payload)

        if excerpt:
            msg.payload.update({
                'excerpt': excerpt,
            })

        # Add information so that we can resolve
        # click through links in the Notification
        # rendering, typically this will be used
        # to bring the user back to this part of
        # the discussion forum

        #
        # IMPORTANT: This can be changed to msg.add_click_link() if we
        # have a URL that we wish to use. In the initial use case,
        # we need to make the link point to a different front end
        #
        msg.add_click_link_params({
            'course_id': course_id,
            'commentable_id': thread.commentable_id,
            'thread_id': thread.id,
        })

        if recipient_user_id:
            # send notification to single user
            publish_notification_to_user(recipient_user_id, msg)

        if recipient_group_id:
            # Send the notification_msg to the CourseGroup via Celery
            # But we can also exclude some users from that list
            if settings.FEATURES.get('ENABLE_NOTIFICATIONS_CELERY', False):
                publish_course_group_notification_task.delay(
                    recipient_group_id,
                    msg,
                    exclude_user_ids=recipient_exclude_user_ids
                )
            else:
                publish_course_group_notification_task(
                    recipient_group_id,
                    msg,
                    exclude_user_ids=recipient_exclude_user_ids
                )
    except Exception, ex:
        # Notifications are never critical, so we don't want to disrupt any
        # other logic processing. So log and continue.
        log.exception(ex)