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_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.º 3
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)
Exemplo n.º 4
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)
Exemplo n.º 5
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.º 6
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
Exemplo n.º 7
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.º 8
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.º 9
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.º 10
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.º 11
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.º 12
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.º 13
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.º 14
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.º 15
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.º 16
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.º 17
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.º 18
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.º 19
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.º 20
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.º 21
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.º 22
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.º 23
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.º 24
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.º 25
0
    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.º 26
0
    def test_marking_read_state(self):
        """
        Verify proper behavior when marking notfications as read/unread
        """

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

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

        # now mark msg as read by this user
        mark_notification_read(self.test_user_id, sent_user_msg.msg.id)

        # shouldn't be counted in unread counts
        self.assertEquals(
            get_notifications_count_for_user(
                self.test_user_id,
                filters={
                    'read': False,
                    'unread': True,
                },
            ), 0)

        # Should be counted in read counts
        self.assertEquals(
            get_notifications_count_for_user(
                self.test_user_id,
                filters={
                    'read': True,
                    'unread': False,
                },
            ), 1)

        # now mark msg as unread by this user
        mark_notification_read(self.test_user_id,
                               sent_user_msg.msg.id,
                               read=False)

        # Should be counted in unread counts
        self.assertEquals(
            get_notifications_count_for_user(
                self.test_user_id,
                filters={
                    'read': False,
                    'unread': True,
                },
            ), 1)

        # Shouldn't be counted in read counts
        self.assertEquals(
            get_notifications_count_for_user(
                self.test_user_id,
                filters={
                    'read': True,
                    'unread': False,
                },
            ), 0)
Exemplo n.º 27
0
    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.º 28
0
    def test_notification_count(self):
        """
        Simple test to make sure that we get the right count back after
        publishing a notification to this test user
        """

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

        # publish
        user_msg = publish_notification_to_user(self.user.id, msg)
        self.assertIsNotNone(user_msg)

        url = reverse('edx_notifications.consumer.notifications.count')

        # now query API
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)

        results = json.loads(response.content.decode('utf-8'))
        self.assertIn('count', results)
        self.assertEqual(results['count'], 1)

        # query just the unread
        response = self.client.get(url, {'read': False, 'unread': True})
        self.assertEqual(response.status_code, 200)

        results = json.loads(response.content.decode('utf-8'))
        self.assertIn('count', results)
        self.assertEqual(results['count'], 1)

        # query just the read, which should be 0
        response = self.client.get(url, {'read': True, 'unread': False})
        self.assertEqual(response.status_code, 200)

        results = json.loads(response.content.decode('utf-8'))
        self.assertIn('count', results)
        self.assertEqual(results['count'], 0)

        # now mark the message as read
        mark_notification_read(self.user.id, user_msg.msg.id)

        # query just the unread, should be 0
        response = self.client.get(url, {'read': False, 'unread': True})
        self.assertEqual(response.status_code, 200)

        results = json.loads(response.content.decode('utf-8'))
        self.assertIn('count', results)
        self.assertEqual(results['count'], 0)

        # query just the read, which should be 1
        response = self.client.get(url, {'read': True, 'unread': False})
        self.assertEqual(response.status_code, 200)

        results = json.loads(response.content.decode('utf-8'))
        self.assertIn('count', results)
        self.assertEqual(results['count'], 1)
Exemplo n.º 29
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, 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)