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_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.º 3
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.º 4
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.º 5
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.º 6
0
    def test_bulk_user_notification_create(self):
        """
        Test that we can create new UserNotifications using an optimized
        code path to minimize round trips to the database
        """

        msg_type = self._save_notification_type()

        # set up some notifications

        msg = 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
            }
        ))

        user_msgs = []
        for user_id in range(const.NOTIFICATION_BULK_PUBLISH_CHUNK_SIZE):
            user_msgs.append(
                UserNotification(user_id=user_id, msg=msg)
            )

        # assert that this only takes one round-trip to the database
        # to insert all of them
        with self.assertNumQueries(1):
            self.provider.bulk_create_user_notification(user_msgs)

        # now make sure that we can query each one
        for user_id in range(const.NOTIFICATION_BULK_PUBLISH_CHUNK_SIZE):
            notifications = self.provider.get_notifications_for_user(user_id)

            self.assertEqual(len(notifications), 1)
            self.assertEqual(notifications[0].msg, msg)

        # now test if we send in a size too large that an exception is raised
        user_msgs.append(
            UserNotification(user_id=user_id, msg=msg)
        )

        with self.assertRaises(BulkOperationTooLarge):
            self.provider.bulk_create_user_notification(user_msgs)
Exemplo n.º 7
0
    def restore_object(self, attrs, instance=None):
        """
        Instantiate a new object from the deserialized data
        """

        if instance is not None:
            raise NotImplementedError()

        user_msg = UserNotification(**attrs)  # pylint: disable=star-args
        return user_msg
Exemplo n.º 8
0
    def to_data_object(self, options=None):  # pylint: disable=unused-argument
        """
        Generate a NotificationType data object
        """

        return UserNotification(
            id=self.id,
            user_id=self.user_id,
            msg=self.msg.to_data_object(),  # pylint: disable=no-member
            read_at=self.read_at,
            user_context=DictField.from_json(self.user_context),
            created=self.created)
Exemplo n.º 9
0
        def _gen_notifications(count, namespace):
            """
            Helper to generate notifications
            """
            for __ in range(count):
                msg = self.provider.save_notification_message(
                    NotificationMessage(namespace=namespace,
                                        msg_type=msg_type,
                                        payload={'foo': 'bar'}))

                self.provider.save_user_notification(
                    UserNotification(user_id=self.test_user_id, msg=msg))
Exemplo n.º 10
0
    def test_archive_the_purged_notifications(self):
        """
        Test to check that deleting user notification should be archive.
        """
        msg_type = self._save_notification_type()
        msg = self.provider.save_notification_message(NotificationMessage(
            namespace='namespace1',
            msg_type=msg_type,
            payload={
                'test': 'test'
            }
        ))

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

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

        self.assertEqual(SQLUserNotificationArchive.objects.all().count(), 0)

        # 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 0 at that moment. because
        # 1 notification has been deleted.
        self.assertEqual(
            self.provider.get_num_notifications_for_user(
                self.test_user_id,
                filters={
                    'namespace': 'namespace1'
                }
            ),
            0
        )
        # Notification should be archived
        # count should be increased by 1.
        self.assertEqual(SQLUserNotificationArchive.objects.all().count(), 1)

        archived_notification = SQLUserNotificationArchive.objects.all()[0]
        self.assertEqual(archived_notification.msg_id, user_notification.msg.id)
        self.assertEqual(archived_notification.user_id, user_notification.user_id)
Exemplo n.º 11
0
    def bulk_dispatch_notification(self, user_ids, msg, exclude_user_ids=None, channel_context=None):
        """
        Perform a bulk dispatch of the notification message to
        all user_ids that will be enumerated over in user_ids.

        NOTE: We will chunk together up to NOTIFICATION_BULK_PUBLISH_CHUNK_SIZE

        user_ids should be a list, a generator function, or a
        django.db.models.query.ValuesQuerySet/ValuesListQuerySet
        when directly feeding in a Django ORM queryset, where we select just the id column of the user
        """

        store = notification_store()

        # get a msg (cloned from original) with resolved links
        msg = self._get_linked_resolved_msg(msg)

        # persist the message in our Store Provide
        _msg = store.save_notification_message(msg)

        user_msgs = []

        exclude_user_ids = exclude_user_ids if exclude_user_ids else []

        cnt = 0
        total = 0

        # enumerate through the list of user_ids and chunk them
        # up. Be sure not to include any user_id in the exclude list
        for user_id in user_ids:
            if user_id not in exclude_user_ids:
                user_msgs.append(
                    UserNotification(
                        user_id=user_id,
                        msg=_msg
                    )
                )
                cnt = cnt + 1
                total = total + 1
                if cnt == const.NOTIFICATION_BULK_PUBLISH_CHUNK_SIZE:
                    store.bulk_create_user_notification(user_msgs)
                    user_msgs = []
                    cnt = 0

        if user_msgs:
            store.bulk_create_user_notification(user_msgs)

        return total
Exemplo n.º 12
0
    def dispatch_notification_to_user(self,
                                      user_id,
                                      msg,
                                      channel_context=None):
        """
        Send a notification to a user, which - in a durable Notification -
        is simply store it in the database, and - soon in the future -
        raise some signal to a waiting client that a message is available
        """

        store = notification_store()

        # get a msg (cloned from original) with resolved links
        msg = self._get_linked_resolved_msg(msg)

        # persist the message in our Store Provide
        _msg = store.save_notification_message(msg)

        # create a new UserNotification and point to the new message
        # this new mapping will have the message in an unread state
        # NOTE: We need to set this up after msg is saved otherwise
        # we won't have it's primary key (id)
        user_msg = UserNotification(user_id=user_id, msg=_msg)

        _user_msg = store.save_user_notification(user_msg)

        #
        # When we support in-broswer push notifications
        # such as Comet/WebSockets, this is where we should
        # signal the client to come fetch the
        # notification that has just been dispatched
        #

        #
        # Here is where we will tie into the Analytics pipeline
        #

        return _user_msg
Exemplo n.º 13
0
    def dispatch_notification_to_user(self,
                                      user_id,
                                      msg,
                                      channel_context=None):
        """
        Send a notification to a user, which - in a TriggerEmailChannel Notification
        """

        # call into one of the registered resolvers to get the email for this
        # user
        scope_results = resolve_user_scope(
            'user_email_resolver', {
                'user_id': user_id,
                'fields': {
                    'user_id': True,
                    'email': True,
                    'first_name': True,
                    'last_name': True,
                }
            })
        msg = self._get_linked_resolved_msg(msg)
        msg.created = datetime.datetime.now(pytz.UTC)

        user_msg = UserNotification(user_id=user_id, msg=msg)
        config = const.NOTIFICATION_DIGEST_GROUP_CONFIG
        for result in scope_results:
            #
            # Do the rendering and the sending of the email
            #
            if isinstance(result, dict):
                email = result['email']
            else:
                email = result

            renderer = get_renderer_for_type(user_msg.msg.msg_type)
            notification_html = ''
            if renderer and renderer.can_render_format(
                    const.RENDER_FORMAT_HTML):
                notification_html = renderer.render(  # pylint: disable=unused-variable
                    user_msg.msg, const.RENDER_FORMAT_HTML, None)
            # create the image dictionary to store the
            # img_path, unique id and title for the image.
            branded_logo = dict(title='Logo',
                                path=const.NOTIFICATION_BRANDED_DEFAULT_LOGO,
                                cid=str(uuid.uuid4()))

            group_name = get_group_name_for_msg_type(
                user_msg.msg.msg_type.name)
            resolve_links = user_msg.msg.resolve_links
            click_link = user_msg.msg.payload['_click_link']

            if resolve_links and not click_link.startswith('http'):
                click_link = const.NOTIFICATION_EMAIL_CLICK_LINK_URL_FORMAT.format(
                    url_path=click_link,
                    encoded_url_path=six.moves.urllib.parse.quote(click_link),
                    user_msg_id=user_msg.id,
                    msg_id=user_msg.msg.id,
                    hostname=const.NOTIFICATION_APP_HOSTNAME)

            context = {
                'branded_logo':
                branded_logo['cid'],
                'notification_html':
                notification_html,
                'user_first_name':
                result['first_name'] if isinstance(result, dict) else None,
                'user_last_name':
                result['last_name'] if isinstance(result, dict) else None,
                'group_name':
                group_name,
                'group_title':
                config['groups'][group_name]['display_name'],
                'click_link':
                click_link
            }

            # render the notifications html template
            email_body = with_inline_css(
                render_to_string(
                    "django/triggered_notification_email/triggered_email.html",
                    context))

            html_part = MIMEMultipart(_subtype='related')
            html_part.attach(MIMEText(email_body, _subtype='html'))
            logo_image = attach_image(branded_logo, 'Header Logo')
            if logo_image:
                html_part.attach(logo_image)

            log.info('Sending Notification email to %s', email)

            msg = EmailMessage(const.NOTIFICATION_TRIGGERED_EMAIL_SUBJECT, '',
                               const.NOTIFICATION_EMAIL_FROM_ADDRESS, [email])
            msg.attach(html_part)
            msg.send()
        return user_msg