def register_notification_types(sender, **kwargs): # pylint: disable=unused-argument """ Register some standard NotificationTypes. This will be called automatically on the Notification subsystem startup (because we are receiving the 'perform_type_registrations' signal) """ register_notification_type( NotificationType( name=u'open-edx.lms.leaderboard.progress.rank-changed', renderer= 'edx_notifications.openedx.leaderboard.LeaderboardRankChangedRenderer', )) register_notification_type( NotificationType( name=u'open-edx.lms.leaderboard.gradebook.rank-changed', renderer= 'edx_notifications.openedx.leaderboard.LeaderboardRankChangedRenderer', )) register_notification_type( NotificationType( name=u'open-edx.lms.leaderboard.engagement.rank-changed', renderer= 'edx_notifications.openedx.leaderboard.LeaderboardRankChangedRenderer', ))
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)
def register_notification_types(sender, **kwargs): # pylint: disable=unused-argument """ Register some standard NotificationTypes. This will be called automatically on the Notification subsystem startup (because we are receiving the 'perform_type_registrations' signal) """ mapping = { NotificationMessageTypes.FILE_UPLOADED: 'GroupProjectFileUploadedRenderer', NotificationMessageTypes.STAGE_OPEN: 'GroupProjectStageOpenRenderer', NotificationMessageTypes.STAGE_DUE: 'GroupProjectStageDueRenderer', NotificationMessageTypes.GRADES_POSTED: 'GroupProjectGradesPostedRenderer', } for message_type, renderer in six.iteritems(mapping): register_notification_type( NotificationType( name=u"{prefix}.{type}".format(prefix=GROUP_PROJECT_V1_NOTIFICATION_PREFIX, type=message_type), renderer="{prefix}.{renderer}".format(prefix=GROUP_PROJECT_RENDERER_PREFIX, renderer=renderer), ) ) # GP v2 can reuse GP v1 renderers register_notification_type( NotificationType( name=u"{prefix}.{type}".format(prefix=GROUP_PROJECT_V2_NOTIFICATION_PREFIX, type=message_type), renderer="{prefix}.{renderer}".format(prefix=GROUP_PROJECT_RENDERER_PREFIX, renderer=renderer), ) )
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 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 )
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 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)
def to_data_object(self, options=None): # pylint: disable=unused-argument """ Generate a NotificationType data object """ return NotificationType(name=self.name, renderer=self.renderer, renderer_context=DictField.from_json( self.renderer_context))
def restore_object(self, attrs, instance=None): """ Instantiate a new object from the deserialized data """ if instance is not None: raise NotImplementedError() return NotificationType(**attrs) # pylint: disable=star-args
def register_notification_types(sender, **kwargs): # pylint: disable=unused-argument """ Register some standard NotificationTypes. This will be called automatically on the Notification subsystem startup (because we are receiving the 'perform_type_registrations' signal) """ # someone replying to thread use-case register_notification_type( NotificationType( name='open-edx.lms.discussions.reply-to-thread', renderer='edx_notifications.openedx.forums.ReplyToThreadRenderer', )) # someone following the thread use-case register_notification_type( NotificationType( name='open-edx.lms.discussions.thread-followed', renderer='edx_notifications.openedx.forums.ThreadFollowedRenderer', )) # new posting in a cohorted/private discussion in the course use-case. register_notification_type( NotificationType( name='open-edx.lms.discussions.cohorted-thread-added', renderer= 'edx_notifications.openedx.forums.CohortedThreadAddedRenderer', )) # new posting in a cohorted/private discussion in the course use-case. register_notification_type( NotificationType( name='open-edx.lms.discussions.cohorted-comment-added', renderer= 'edx_notifications.openedx.forums.CohortedCommentAddedRenderer', )) # someone voting the thread use-case register_notification_type( NotificationType( name='open-edx.lms.discussions.post-upvoted', renderer='edx_notifications.openedx.forums.PostUpvotedRenderer', )) # someone voting the comment use-case register_notification_type( NotificationType( name='open-edx.lms.discussions.comment-upvoted', renderer='edx_notifications.openedx.forums.CommentUpvotedRenderer', )) # someone flagging a post use-case register_notification_type( NotificationType( name='open-edx.lms.discussions.post-flagged', renderer='edx_notifications.openedx.forums.PostFlaggedRenderer', ))
def setUp(self): """ Setup the tests values. """ self.provider = SQLNotificationStoreProvider() self.test_user_id = 1 self.notification_type = NotificationType( name='foo.bar.baz', renderer='foo.renderer', renderer_context={'param1': 'value1'}, )
def perform_type_registrations_handler(sender, **kwargs): # pylint: disable=unused-argument """ Register test notification types """ logger.info('Registering NotificationTypes...') register_notification_type( NotificationType( name='testserver.type1', renderer='edx_notifications.renderers.basic.BasicSubjectBodyRenderer', ) ) register_notification_type( NotificationType( name='testserver.msg-with-resolved-click-link', renderer='edx_notifications.renderers.basic.BasicSubjectBodyRenderer', ) )
def test_get_provider(self): """ Makes sure we get an instance of the registered store provider """ provider = get_notification_channel(self.test_user_id, self.test_msg_type) self.assertIsNotNone(provider) self.assertTrue(isinstance(provider, BaseDurableNotificationChannel)) self.assertEqual(provider.name, 'channel6') self.assertEqual(provider.display_name, 'channel_name6') self.assertEqual(provider.display_description, 'channel_description6') # now verify that the wildcard hierarchy rules # work, by making a msg_type name which will match one of # the intermediate items in the hierarchy provider = get_notification_channel( self.test_user_id, NotificationType( name='edx_notifications.channels.tests.another_one', renderer='foo.renderer', ) ) self.assertEqual(provider.name, 'channel3') self.assertEqual(provider.display_name, 'channel_name3') self.assertEqual(provider.display_description, 'channel_description3') provider = get_notification_channel( self.test_user_id, NotificationType( name='edx_notifications.channels.diff_subpath.diff_leaf', renderer='foo.renderer', ) ) self.assertEqual(provider.name, 'channel2') self.assertEqual(provider.display_name, 'channel_name2') self.assertEqual(provider.display_description, 'channel_description2')
def setUp(self): """ Harnessing """ reset_notification_channels() self.test_user_id = 1001 # an arbitrary user_id self.test_msg_type = NotificationType( name='edx_notifications.channels.tests.test_channel.channeltests.foo', renderer='foo.renderer', ) self.addCleanup(reset_notification_channels)
def setUp(self): """ Build out test harnessing """ self.msg_type = NotificationType( name='open-edx.edx_notifications.lib.tests.test_publisher', renderer='edx_notifications.renderers.basic.BasicSubjectBodyRenderer', ) register_notification_type(self.msg_type) super(ConsumerAPITests, self).setUp()
def setUp(self): """ Initialize some data """ clear_renderers() self.test_user_id = 1001 # some bogus user identifier self.msg_type = NotificationType( name='open-edx.edx_notifications.lib.tests.test_publisher', renderer='edx_notifications.renderers.basic.BasicSubjectBodyRenderer', ) register_notification_type(self.msg_type)
def register_notification_types(sender, **kwargs): # pylint: disable=unused-argument """ Register some standard NotificationTypes. This will be called automatically on the Notification subsystem startup (because we are receiving the 'perform_type_registrations' signal) """ register_notification_type( NotificationType( name='open-edx.mobileapps.notifications', renderer= 'edx_notifications.renderers.basic.JsonRenderer' # using this for tests to pass. ))
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 _save_notification_type(self): """ Helper to set up a notification_type """ notification_type = NotificationType( name='foo.bar.baz', renderer='foo.renderer', renderer_context={'param1': 'value1'}, ) result = self.provider.save_notification_type(notification_type) return result
def register_notification_types(sender, **kwargs): # pylint: disable=unused-argument """ Register some standard NotificationTypes. This will be called automatically on the Notification subsystem startup (because we are receiving the 'perform_type_registrations' signal) """ # updates/announcements in the course use-case. register_notification_type( NotificationType( name='open-edx.studio.announcements.new-announcement', renderer= 'edx_notifications.openedx.course_announcements.NewCourseAnnouncementRenderer', ))
def setUp(self): """ Sets up test environments """ startup.initialize() self.msg_type = NotificationType( name='open-edx.studio.announcements.new-announcement', renderer='edx_notifications.openedx.course_announcements.NewCourseAnnouncementRenderer' ) register_notification_type(self.msg_type) self.msg = NotificationMessage( namespace='foo/bar/baz', msg_type=self.msg_type, payload=CANNED_TEST_PAYLOAD['open-edx.studio.announcements.new-announcement'] )
def test_from_data_object(self): """ Make sure we can hydrate a SQLNotificationMessage from a NotificationMessage """ msg_type = NotificationType( name='foo.bar.baz', renderer='foo.renderer', ) msg = NotificationMessage(id=2, msg_type=msg_type) orm_obj = SQLNotificationMessage.from_data_object(msg) self.assertEqual(orm_obj.id, msg.id)
def test_message_serialization(self): """ Test serialization/deserialization of a sample Notification Message """ msg = NotificationMessage( id=1001, msg_type=NotificationType( name='edx_notifications.sample', renderer='foo.renderer', ), namespace='my-namespace', payload={ 'name1': 'val1', 'name2': datetime.utcnow(), }, deliver_no_earlier_than=datetime.utcnow(), created=datetime.utcnow(), ) serializer = NotificationMessageSerializer(msg) json = JSONRenderer().render(serializer.data) # no deserialize the string and compare resulting objects stream = BytesIO(json) data = JSONParser().parse(stream) deserializer = NotificationMessageSerializer(data=data) self.assertTrue(deserializer.is_valid()) # compare the original data object to our deserialized version # and make sure they are the same msg_output = deserializer.object self.assertEqual(msg, msg_output) self.assertEqual(msg.msg_type, msg_output.msg_type) # pylint: disable=maybe-no-member # now intentionally try to break it data['namespace'] = 'busted' data['msg_type']['name'] = 'not-same' deserializer = NotificationMessageSerializer(data=data) self.assertTrue(deserializer.is_valid()) # compare the original data object to our deserialized version # and make sure they are not considered the same msg_output = deserializer.object self.assertNotEqual(msg, msg_output) self.assertNotEqual(msg.msg_type, msg_output.msg_type) # pylint: disable=maybe-no-member
def test_underscore(self): """ Tests on the UnderscoreStaticFileRenderer """ renderer = TestUnderscoreStaticFileRenderer( 'basic_subject_body.underscore') self.assertTrue(renderer.can_render_format(RENDER_FORMAT_HTML)) msg_type = NotificationType( name='open-edx.edx_notifications.lib.tests.test_publisher', renderer= 'edx_notifications.renderers.basic.BasicSubjectBodyRenderer', ) register_notification_type(msg_type) self.assertIsNotNone(get_renderer_for_type(msg_type)) msg = NotificationMessage(namespace='test-runner', msg_type=msg_type, payload={ 'subject': 'test subject', 'body': 'test body', }, created=datetime.datetime.now(pytz.UTC)) renderer.get_template_path(RENDER_FORMAT_HTML) with self.assertRaises(NotImplementedError): renderer.get_template_path(RENDER_FORMAT_SMS) with self.assertRaises(NotImplementedError): renderer.render(msg, RENDER_FORMAT_SMS, None) html = renderer.render(msg, RENDER_FORMAT_HTML, None) with self.assertRaises(NotImplementedError): renderer.get_template_path(RENDER_FORMAT_SMS) self.assertIn('test subject', html) self.assertIn('test body', html) self.assertIn("<div class='xns-title'>", html) self.assertIn("<div class='xns-body'>", html) with self.assertRaises(Exception): bad_renderer = TestUnderscoreStaticFileRenderer('foo.underscore') bad_renderer.render(msg, RENDER_FORMAT_HTML, None)
def test_update_notification_type(self): """ Assert that we cannot change a notification type """ notification_type = NotificationType( name='foo.bar.baz', renderer='foo.renderer', ) with self.assertNumQueries(3): self.provider.save_notification_type(notification_type) # This should be fine saving again, since nothing is changing with self.assertNumQueries(3): self.provider.save_notification_type(notification_type)
def setUp(self): """ start up stuff """ register_user_scope_resolver('list_scope', TestListScopeResolver()) self.store = notification_store() self.callback = NotificationDispatchMessageCallback() self.msg_type = self.store.save_notification_type( NotificationType( name='foo.bar', renderer='foo', )) self.msg = self.store.save_notification_message( NotificationMessage( msg_type=self.msg_type, payload={'foo': 'bar'}, )) self.timer_for_user = NotificationCallbackTimer( context={ 'msg_id': self.msg.id, 'distribution_scope': { 'scope_name': 'user', 'scope_context': { 'user_id': 1 } } }) self.timer_for_group = NotificationCallbackTimer( context={ 'msg_id': self.msg.id, 'distribution_scope': { 'scope_name': 'list_scope', 'scope_context': { 'range': 5 } } })
def test_null_channel(self): """ Makes sure that the NullNotificationChannel doesn't do anythign what so ever """ test_msg_type = NotificationType( name='edx_notifications.channels.tests.test_channel.channeltests.null', renderer='foo.renderer', ) provider = get_notification_channel(self.test_user_id, test_msg_type) self.assertIsNotNone(provider) self.assertTrue(isinstance(provider, NullNotificationChannel)) self.assertIsNone(provider.dispatch_notification_to_user(None, None)) self.assertEqual(provider.bulk_dispatch_notification(None, None), 0) self.assertIsNone(provider.resolve_msg_link(None, None, None))
def setUp(self): """ Test setup """ startup.initialize(register_system_types=False) self.msg_type = NotificationType( name='open-edx.edx_notifications.lib.tests.test_publisher', renderer='edx_notifications.renderers.basic.JsonRenderer', ) register_notification_type(self.msg_type) self.msg = NotificationMessage(namespace='test-runner', msg_type=self.msg_type, payload={ 'foo': 'bar', 'one': 'two' })
def get_notification_type(message_type): return NotificationType(name=message_type, renderer='', renderer_context={})
def publish_notification(cls, namespace, msg_type_name, payload, parse_channel_ids, send_at=None, timer_name=None): """ Helper class method to hide some of the inner workings of this channel This will work with immediate or timer based publishing. 'namespace' is an instance of NotificationMessage 'msg_type' is the type name of the NotificationMessage 'payload' is the raw data dictionary to send over the mobile clients 'parse_channel_ids' is a list of Parse channel_ids, which are subscription lists, not to be confused with edx-notification's NotificationChannels - an unfortunate semantic collision. 'send_at' is a datetime when this notification should be sent. Note that firing of notifications is approximate, so it will not fire BEFORE send_at, but there might be a lag, depending on how frequent timer polling is configured in a runtime instance. 'timer_name' can be used in conjunction with 'send_at'. This is to allow for a fixed timer identifier in case the timed notification needs to be updated (or deleted) """ try: msg_type = get_notification_type(msg_type_name) except ItemNotFoundError: msg_type = NotificationType( name=msg_type_name, renderer='edx_notifications.renderers.basic.JsonRenderer') register_notification_type(msg_type) msg = NotificationMessage(namespace=namespace, msg_type=msg_type, payload=payload) if not send_at: # send immediately publish_notification_to_user( user_id=_PARSE_SERVICE_USER_ID, msg=msg, # we want to make sure we always call this channel provider preferred_channel=_PARSE_CHANNEL_NAME, channel_context={ # tunnel through the parse_channel_id through the # channel context 'parse_channel_ids': parse_channel_ids, }) else: # time-based sending, use a TimedNotification publish_timed_notification( msg=msg, send_at=send_at, scope_name='user', scope_context={'user_id': _PARSE_SERVICE_USER_ID}, timer_name=timer_name, timer_context={ # we want to make sure we always call this channel provider 'preferred_channel': _PARSE_CHANNEL_NAME, 'channel_context': { # tunnel through the parse_channel_id through # through the channel context 'parse_channel_ids': parse_channel_ids, } })