def test_publish_notification(self): """ Go through and set up a notification and publish it """ msg = NotificationMessage( namespace='test-runner', msg_type=self.msg_type, payload={ 'foo': 'bar' } ) # 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) # make sure type checking is happening with self.assertRaises(ContractNotRespected): get_notifications_count_for_user('bad-type') # now query back the notification to make sure it got stored # and we can retrieve it self.assertEqual( get_notifications_count_for_user(self.test_user_id), 1 ) # make sure it asserts that user_id is an integer with self.assertRaises(ContractNotRespected): get_notifications_for_user('bad-id') 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) self.assertEqual(read_user_msg.msg, sent_user_msg.msg)
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'))
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))
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))
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))
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() 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(): notifications = get_notifications_for_user(user.id) self.assertTrue(isinstance(notifications, list)) self.assertEqual(len(notifications), 1) self.assertTrue(isinstance(notifications[0], UserNotification))
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))
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))
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))
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"))
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))
def test_publish_notification(self): """ Go through and set up a notification and publish it """ msg = NotificationMessage(namespace="test-runner", msg_type=self.msg_type, payload={"foo": "bar"}) # 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) # make sure type checking is happening with self.assertRaises(ContractNotRespected): get_notifications_count_for_user("bad-type") # 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) # make sure it asserts that user_id is an integer with self.assertRaises(ContractNotRespected): get_notifications_for_user("bad-id") 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) self.assertEqual(read_user_msg.msg, sent_user_msg.msg)
def get(self, request): """ HTTP GET Handler """ try: filters, options = _get_filter_and_options(request) except ValueError: return Response({}, status.HTTP_400_BAD_REQUEST) user_msgs = get_notifications_for_user( int(request.user.id), filters=filters, options=options, ) resultset = [user_msg.get_fields() for user_msg in user_msgs] return Response(resultset, status.HTTP_200_OK)
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( [user_id for user_id in 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))
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 = [user_id for user_id in range(1, const.NOTIFICATION_BULK_PUBLISH_CHUNK_SIZE * 2 + 1)] exclude_user_ids = [user_id for user_id in 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))
def _send_user_digest(namespace_info, from_timestamp, to_timestamp, user_id, email, first_name, last_name, subject, from_email, unread_only=True): """ This will send a digest of unread notifications to a given user. Note, it is assumed here that the user's preference has already been checked. namespace_info will contain a 'display_name' entry which will be a human readable string that can be put in the digest email """ # query all unread notifications for this user since the timestamp notifications = get_notifications_for_user( user_id, filters={ 'read': not unread_only, 'unread': True, 'start_date': from_timestamp, 'end_timestamp': to_timestamp, 'namespace': namespace_info['namespace'], }, options={ 'select_related': True, # make sure we do JOINs on the initial query } ) notification_groups = render_notifications_by_type(notifications) # As an option, don't send an email at all if there are no # unread notifications if not notification_groups and const.NOTIFICATION_DONT_SEND_EMPTY_DIGEST: log.debug('Digest email for {email} is empty. Not sending...'.format(email=email)) return 0 context = { 'namespace_display_name': namespace_info['display_name'], 'grouped_user_notifications': notification_groups } # render the notifications html template notifications_html = render_to_string("django/digests/unread_notifications_inner.html", context) # 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())) context = { 'branded_logo': branded_logo['cid'], 'user_first_name': first_name, 'user_last_name': last_name, 'namespace': namespace_info['display_name'], 'count': len(notifications), 'rendered_notifications': notifications_html } # render the mail digest template. email_body = with_inline_css( render_to_string("django/digests/branded_notifications_outer.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 Digest email to {email}'.format(email=email)) # do a formatting pass on the email subject in case we need # to also present the namespace display_name subject = subject.format(display_name=namespace_info['display_name']) msg = EmailMessage(subject, None, from_email, [email]) msg.attach(html_part) msg.send() return 1