示例#1
0
    def test_basic(self):
        transactional_message = Message(options={u'transactional': True}, **self.msg_kwargs)
        for key in self.msg_kwargs:
            self.assertEqual(getattr(transactional_message, key), self.msg_kwargs.get(key))
        self.assertIsNotNone(transactional_message.uuid)
        assert transactional_message.options.get(u'transactional')  # pylint: disable=no-member

        normal_message = Message(**self.msg_kwargs)
        assert not dict(normal_message.options)
示例#2
0
    def test_log_level(self, log_level, expect_log_warn, expect_log_debug):
        logging.getLogger().setLevel(logging.INFO)

        self.msg_kwargs[u'log_level'] = log_level
        message = Message(**self.msg_kwargs)
        logger = message.get_message_specific_logger(LOG)
        with patch(u'logging.Logger.callHandlers') as mock_log:
            logger.warning(u'Test warning statement')
            self.assertEqual(mock_log.called, expect_log_warn)
        with patch(u'logging.Logger.callHandlers') as mock_log:
            logger.debug(u'Test debug statement')
            self.assertEqual(mock_log.called, expect_log_debug)
示例#3
0
    def test_ace_send_happy_path(self):
        patch_policies(self, [StubPolicy([ChannelType.PUSH])])
        mock_channel = Mock(
            channel_type=ChannelType.EMAIL,
            action_links=[],
            tracker_image_sources=[],
        )

        recipient = Recipient(username=u'testuser')
        msg = Message(
            app_label=u'testapp',
            name=u'testmessage',
            recipient=recipient,
        )

        channel_map = ChannelMap([
            [u'sailthru_email', mock_channel],
        ])

        with patch(u'edx_ace.channel.channels', return_value=channel_map):
            ace.send(msg)

        mock_channel.deliver.assert_called_once_with(
            msg,
            RenderedEmail(
                from_name=u'template from_name.txt',
                subject=u'template subject.txt',
                # The new lines are needed because the template has some tags, which means leftover newlines
                body_html=u'template body.html\n\n\n\n\n',
                head_html=u'template head.html\n',
                body=u'template body.txt',
            ),
        )
示例#4
0
def _course_update_schedule_send(site_id, msg_str):
    site = Site.objects.get(pk=site_id)
    if not ScheduleConfig.current(site).deliver_course_update:
        return

    msg = Message.from_string(msg_str)
    ace.send(msg)
示例#5
0
    def test_happy_path(self):
        channel = FileEmailChannel()
        rendered_message = RenderedEmail(
            from_name=u'Robot',
            head_html=u'<img src="https://tracker/.img" />',
            subject=u'\n Hello from  \r\nRobot ! \n',
            body=u'Just trying to see what is like to talk to a human!',
            body_html=u"""
                <p>Just trying to see what is like to talk to a human!</p>

                <hr />
            """,
        )

        with NamedTemporaryFile('r+') as f:
            message = Message(
                app_label=u'testapp',
                name=u'testmessage',
                options={
                    u'from_address': u'*****@*****.**',
                    PATH_OVERRIDE_KEY: f.name,
                },
                recipient=Recipient(username=u'Robot',
                                    email_address=u'*****@*****.**'),
            )

            channel.deliver(message, rendered_message)

            contents = f.read()
            assert u'subject: Hello from' in contents
            assert u'to: [email protected]' in contents
            assert u'body: Just trying to see what' in contents
            assert u'talk to a human!</p>' in contents
示例#6
0
    def _deserialize_field(cls, field_name, field_value):
        u"""
        Deserialize a single encoded field value for this message.

        Arguments:
            field_name (str): The name of the field being decoded.
            field_value: The value of field being deserialized.
        """
        # We have to import these here to avoid a circular dependency since these classes use this mixin.
        from edx_ace.recipient import Recipient
        from edx_ace.message import Message

        if field_value is None:
            return None

        if field_name == u'expiration_time':
            return date.deserialize(field_value)
        elif field_name in (u'uuid', u'send_uuid'):
            return UUID(field_value)
        # TODO(later): should this be more dynamic?
        elif field_name == u'message':
            return Message(**field_value)
        elif field_name == u'recipient':
            return Recipient(**field_value)
        else:
            return field_value
示例#7
0
    def test_ace_send_happy_path(self, _mock_get_template):
        patch_policies(self, [StubPolicy([ChannelType.PUSH])])
        mock_channel = Mock(
            name=u'test_channel',
            channel_type=ChannelType.EMAIL
        )
        patch_channels(self, [mock_channel])

        recipient = Recipient(username=u'testuser')
        msg = Message(
            app_label=u'testapp',
            name=u'testmessage',
            recipient=recipient,
        )
        ace.send(msg)
        mock_channel.deliver.assert_called_once_with(
            msg,
            RenderedEmail(
                from_name=TEMPLATES[u'testapp/edx_ace/testmessage/email/from_name.txt'].render(),
                subject=TEMPLATES[u'testapp/edx_ace/testmessage/email/subject.txt'].render(),
                body_html=TEMPLATES[u'testapp/edx_ace/testmessage/email/body.html'].render(),
                head_html=TEMPLATES[u'testapp/edx_ace/testmessage/email/head.html'].render(),
                body=TEMPLATES[u'testapp/edx_ace/testmessage/email/body.txt'].render(),
            ),
        )
示例#8
0
def _upgrade_reminder_schedule_send(site_id, msg_str):
    site = Site.objects.get(pk=site_id)
    if not ScheduleConfig.current(site).deliver_upgrade_reminder:
        return

    msg = Message.from_string(msg_str)
    ace.send(msg)
示例#9
0
def _upgrade_reminder_schedule_send(site_id, msg_str):
    site = Site.objects.get(pk=site_id)
    if not ScheduleConfig.current(site).deliver_upgrade_reminder:
        return

    msg = Message.from_string(msg_str)
    ace.send(msg)
示例#10
0
def _course_update_schedule_send(site_id, msg_str):
    site = Site.objects.get(pk=site_id)
    if not ScheduleConfig.current(site).deliver_course_update:
        return

    msg = Message.from_string(msg_str)
    ace.send(msg)
示例#11
0
def _recurring_nudge_schedule_send(site_id, msg_str):
    site = Site.objects.get(pk=site_id)
    if not ScheduleConfig.current(site).deliver_recurring_nudge:
        LOG.debug('Recurring Nudge: Message delivery disabled for site %s', site.domain)
        return

    msg = Message.from_string(msg_str)
    LOG.debug('Recurring Nudge: Sending message = %s', msg_str)
    ace.send(msg)
示例#12
0
def _recurring_nudge_schedule_send(site_id, msg_str):
    site = Site.objects.get(pk=site_id)
    if not ScheduleConfig.current(site).deliver_recurring_nudge:
        LOG.debug('Recurring Nudge: Message delivery disabled for site %s', site.domain)
        return

    msg = Message.from_string(msg_str)
    LOG.debug('Recurring Nudge: Sending message = %s', msg_str)
    ace.send(msg)
示例#13
0
    def test_ace_send_unsupported_channel(self, *_args):
        recipient = Recipient(username=u'testuser')
        msg = Message(
            app_label=u'testapp',
            name=u'testmessage',
            recipient=recipient,
        )

        ace.send(msg)  # UnsupportedChannelError shouldn't throw UnsupportedChannelError
示例#14
0
    def test_default_channel(self):
        channel_map = ChannelMap([
            ['sailthru', SailthruEmailChannel],
        ])

        message = Message(options={u'transactional': True}, **self.msg_kwargs)

        with patch(u'edx_ace.channel.channels', return_value=channel_map):
            channel = get_channel_for_message(ChannelType.EMAIL, message)
            assert channel is SailthruEmailChannel
示例#15
0
    def test_get_channel_for_message(self):
        channel_map = ChannelMap([
            [u'file_email', FileEmailChannel],
            [u'sailthru_email', SailthruEmailChannel],
        ])

        transactional_msg = Message(options={u'transactional': True},
                                    **self.msg_kwargs)
        info_msg = Message(options={}, **self.msg_kwargs)

        with patch(u'edx_ace.channel.channels', return_value=channel_map):
            assert get_channel_for_message(
                ChannelType.EMAIL, transactional_msg) is FileEmailChannel
            assert get_channel_for_message(ChannelType.EMAIL,
                                           info_msg) is SailthruEmailChannel

            with self.assertRaises(UnsupportedChannelError):
                assert get_channel_for_message(ChannelType.PUSH,
                                               transactional_msg)
示例#16
0
 def create_test_message(self):
     return Message(
         app_label='foo',
         name='bar',
         recipient=Recipient(
             username=self.student.username,
             email_address=self.student.email,
         ),
         context={'course_ids': [str(self.course.id)]},
     )
示例#17
0
    def test_with_no_from_address_with_default(self):
        message = Message(
            app_label='testapp',
            name='testmessage',
            options={},
            recipient=Recipient(lms_user_id=123, email_address='*****@*****.**'),
        )

        self.channel.deliver(message, self.mock_rendered_message)
        assert len(mail.outbox) == 1, 'Should have one email'
示例#18
0
    def _get_rendered_message(self):
        channel = DjangoEmailChannel()
        message = Message(
            app_label='testapp',
            name='testmessage',
            options={},
            recipient=Recipient(lms_user_id=123, email_address='*****@*****.**'),
        )

        return render(channel, message)
示例#19
0
    def test_with_no_from_address_without_default(self):
        message = Message(
            app_label='testapp',
            name='testmessage',
            options={},
            recipient=Recipient(lms_user_id=123, email_address='*****@*****.**'),
        )

        with self.assertRaises(FatalChannelDeliveryError):
            self.channel.deliver(message, self.mock_rendered_message)
示例#20
0
def _schedule_send(msg_str, site_id, delivery_config_var, log_prefix):  # lint-amnesty, pylint: disable=missing-function-docstring
    site = Site.objects.select_related('configuration').get(pk=site_id)
    if _is_delivery_enabled(site, delivery_config_var, log_prefix):
        msg = Message.from_string(msg_str)

        user = User.objects.get(id=msg.recipient.lms_user_id)
        with emulate_http_request(site=site, user=user):
            _annonate_send_task_for_monitoring(msg)
            LOG.debug('%s: Sending message = %s', log_prefix, msg_str)
            ace.send(msg)
            _track_message_sent(site, user, msg)
示例#21
0
def _schedule_send(msg_str, site_id, delivery_config_var, log_prefix):
    site = Site.objects.select_related('configuration').get(pk=site_id)
    if _is_delivery_enabled(site, delivery_config_var, log_prefix):
        msg = Message.from_string(msg_str)

        user = User.objects.get(username=msg.recipient.username)
        with emulate_http_request(site=site, user=user):
            _annonate_send_task_for_monitoring(msg)
            LOG.debug('%s: Sending message = %s', log_prefix, msg_str)
            ace.send(msg)
            _track_message_sent(site, user, msg)
示例#22
0
def _schedule_send(msg_str, site_id, delivery_config_var, log_prefix):
    site = Site.objects.select_related('configuration').get(pk=site_id)
    if _is_delivery_enabled(site, delivery_config_var, log_prefix):
        msg = Message.from_string(msg_str)

        user = User.objects.get(username=msg.recipient.username)
        with emulate_http_request(site=site, user=user):
            _annonate_send_task_for_monitoring(msg)
            LOG.debug(u'%s: Sending message = %s', log_prefix, msg_str)
            ace.send(msg)
            _track_message_sent(site, user, msg)
示例#23
0
    def test_no_recipient_email_address(self):
        message = Message(
            app_label='testapp',
            name='testmessage',
            options={},
            recipient=Recipient(lms_user_id=123),
        )
        rendered_email = render(self.channel, message)

        with self.assertRaisesRegex(InvalidMessageError, 'No email address'):
            deliver(self.channel, rendered_email, message)
示例#24
0
def send_ace_message(goal):
    """
    Send an email reminding users to stay on track for their learning goal in this course

    Arguments:
        goal (CourseGoal): Goal object
    """
    user = goal.user
    try:
        course = CourseOverview.get_from_id(goal.course_key)
    except CourseOverview.DoesNotExist:
        log.error("Goal Reminder course not found.")

    course_name = course.display_name

    site = Site.objects.get_current()
    message_context = get_base_template_context(site)

    course_home_url = reverse(course_home_url_name(course.id),
                              args=[str(course.id)])
    course_home_absolute_url_parts = ("https", site.name, course_home_url, '',
                                      '', '')
    course_home_absolute_url = six.moves.urllib.parse.urlunparse(
        course_home_absolute_url_parts)

    goals_unsubscribe_url = reverse('course-home:unsubscribe-from-course-goal',
                                    kwargs={'token': goal.unsubscribe_token})

    message_context.update({
        'email':
        user.email,
        'platform_name':
        configuration_helpers.get_value('PLATFORM_NAME',
                                        settings.PLATFORM_NAME),
        'course_name':
        course_name,
        'days_per_week':
        goal.days_per_week,
        'course_url':
        course_home_absolute_url,
        'goals_unsubscribe_url':
        goals_unsubscribe_url,
        'unsubscribe_url':
        None,  # We don't want to include the default unsubscribe link
    })

    msg = Message(name="goalreminder",
                  app_label="course_goals",
                  recipient=Recipient(user.id, user.email),
                  language=get_user_preference(user, LANGUAGE_KEY),
                  context=message_context)

    with emulate_http_request(site, user):
        ace.send(msg)
示例#25
0
def _recurring_nudge_schedule_send(site_id, msg_str):
    site = Site.objects.get(pk=site_id)
    if not ScheduleConfig.current(site).deliver_recurring_nudge:
        LOG.debug('Recurring Nudge: Message delivery disabled for site %s', site.domain)
        return

    msg = Message.from_string(msg_str)
    # A unique identifier for this batch of messages being sent.
    set_custom_metric('send_uuid', msg.send_uuid)
    # A unique identifier for this particular message.
    set_custom_metric('uuid', msg.uuid)
    LOG.debug('Recurring Nudge: Sending message = %s', msg_str)
    ace.send(msg)
示例#26
0
    def setUp(self):
        super(TestDelivery, self).setUp()

        self.mock_channel = Mock(name=u'test_channel',
                                 channel_type=ChannelType.EMAIL)
        patch_channels(self, [self.mock_channel])
        self.recipient = Recipient(username=str(sentinel.username))
        self.message = Message(
            app_label=str(sentinel.app_label),
            name=str(sentinel.name),
            recipient=self.recipient,
        )
        self.current_time = datetime.datetime.utcnow().replace(tzinfo=tzutc())
示例#27
0
    def test_render_email_with_sailthru(self):
        message = Message(
            app_label=u'testapp',
            name=u'testmessage',
            options={},
            recipient=Recipient(username=u'Robot', email_address=u'*****@*****.**'),
        )

        rendered_email = render(self.channel, message)

        assert u'{beacon_src}' in rendered_email.body_html
        assert u'{view_url}' in rendered_email.body_html
        assert u'{optout_confirm_url}' in rendered_email.body_html
示例#28
0
 def _send_message_task(self, schedule, offset, target_day):
     """
     Calls the task for sending a message to the given schedule and for the given
     offset and target_day. Returns the message that would have been sent.
     """
     sent_messages = []
     with patch.object(self.task, 'async_send_task') as mock_schedule_send:
         mock_schedule_send.apply_async = lambda args, *_a, **_kw: sent_messages.append(args[1])
         self.task().apply(kwargs=dict(
             site_id=self.site_config.site.id, target_day_str=serialize(target_day), day_offset=offset,
             bin_num=self._calculate_bin_for_user(schedule.enrollment.user),
         ))
     self.assertEqual(len(sent_messages), 1)
     return Message.from_string(sent_messages[0])
示例#29
0
 def _send_message_task(self, schedule, offset, target_day):
     """
     Calls the task for sending a message to the given schedule and for the given
     offset and target_day. Returns the message that would have been sent.
     """
     sent_messages = []
     with patch.object(self.task, 'async_send_task') as mock_schedule_send:
         mock_schedule_send.apply_async = lambda args, *_a, **_kw: sent_messages.append(args[1])
         self.task().apply(kwargs=dict(
             site_id=self.site_config.site.id, target_day_str=serialize(target_day), day_offset=offset,
             bin_num=self._calculate_bin_for_user(schedule.enrollment.user),
         ))
     self.assertEqual(len(sent_messages), 1)
     return Message.from_string(sent_messages[0])
示例#30
0
def send_calendar_sync_email(self, msg_string, from_address=None):
    """
    Sending calendar sync email to the user.
    """
    msg = Message.from_string(msg_string)

    max_retries = settings.RETRY_CALENDAR_SYNC_EMAIL_MAX_ATTEMPTS
    retries = self.request.retries

    if from_address is None:
        from_address = configuration_helpers.get_value(
            'ACTIVATION_EMAIL_FROM_ADDRESS') or (
                configuration_helpers.get_value('email_from_address',
                                                settings.DEFAULT_FROM_EMAIL))
    msg.options['from_address'] = from_address

    dest_addr = msg.recipient.email_address

    site = Site.objects.get_current()
    user = User.objects.get(username=msg.recipient.username)

    try:
        with emulate_http_request(site=site, user=user):
            ace.send(msg)
        # Log that the Activation Email has been sent to user without an exception
        log.info(
            "Calendar Sync Email has been sent to User {user_email}".format(
                user_email=dest_addr))
    except RecoverableChannelDeliveryError:
        log.info(
            'Retrying sending email to user {dest_addr}, attempt # {attempt} of {max_attempts}'
            .format(dest_addr=dest_addr,
                    attempt=retries,
                    max_attempts=max_retries))
        try:
            self.retry(countdown=settings.RETRY_ACTIVATION_EMAIL_TIMEOUT,
                       max_retries=max_retries)
        except MaxRetriesExceededError:
            log.error(
                'Unable to send calendar sync email to user from "%s" to "%s"',
                from_address,
                dest_addr,
                exc_info=True)
    except Exception:
        log.exception(
            'Unable to send calendar sync email to user from "%s" to "%s"',
            from_address,
            dest_addr,
        )
        raise Exception
示例#31
0
def _recurring_nudge_schedule_send(site_id, msg_str):
    site = Site.objects.get(pk=site_id)
    if not ScheduleConfig.current(site).deliver_recurring_nudge:
        LOG.debug('Recurring Nudge: Message delivery disabled for site %s',
                  site.domain)
        return

    msg = Message.from_string(msg_str)
    # A unique identifier for this batch of messages being sent.
    set_custom_metric('send_uuid', msg.send_uuid)
    # A unique identifier for this particular message.
    set_custom_metric('uuid', msg.uuid)
    LOG.debug('Recurring Nudge: Sending message = %s', msg_str)
    ace.send(msg)
示例#32
0
    def test_on_behalf_option_with_sailthru(self, message_options, expected_options):
        """
        Tests sailthru send API is called with on_behalf option
        """
        message = Message(
            app_label=u'testapp',
            name=u'testmessage',
            options=message_options,
            recipient=Recipient(username=u'Robot', email_address=u'*****@*****.**'),
        )
        rendered_email = render(self.channel, message)

        with patch('edx_ace.channel.sailthru.SailthruClient.send') as mock_send:
            deliver(self.channel, rendered_email, message)
            self.assertEqual(mock_send.call_args_list[0][1]['options'], expected_options)
示例#33
0
    def setUp(self):
        super(TestDelivery, self).setUp()

        self.mock_channel = Mock(name=u'test_channel',
                                 channel_type=ChannelType.EMAIL)
        self.recipient = Recipient(username=str(sentinel.username))
        self.message = Message(
            app_label=str(sentinel.app_label),
            name=str(sentinel.name),
            options={
                u'from_address': u'*****@*****.**',
            },
            recipient=self.recipient,
        )
        self.current_time = datetime.datetime.utcnow().replace(tzinfo=tzutc())
示例#34
0
def _schedule_send(msg_str, site_id, delivery_config_var, log_prefix):
    site = Site.objects.get(pk=site_id)
    if _is_delivery_enabled(site, delivery_config_var, log_prefix):
        msg = Message.from_string(msg_str)

        user = User.objects.get(username=msg.recipient.username)
        middleware_classes = [
            CurrentRequestUserMiddleware,
            CurrentSiteThemeMiddleware,
        ]
        with emulate_http_request(site=site,
                                  user=user,
                                  middleware_classes=middleware_classes):
            _annonate_send_task_for_monitoring(msg)
            LOG.debug('%s: Sending message = %s', log_prefix, msg_str)
            ace.send(msg)
示例#35
0
def send_activation_email(self, msg_string, from_address=None):
    """
    Sending an activation email to the user.
    """
    msg = Message.from_string(msg_string)

    max_retries = settings.RETRY_ACTIVATION_EMAIL_MAX_ATTEMPTS
    retries = self.request.retries

    if from_address is None:
        from_address = configuration_helpers.get_value('ACTIVATION_EMAIL_FROM_ADDRESS') or (
            configuration_helpers.get_value('email_from_address', settings.DEFAULT_FROM_EMAIL)
        )
    msg.options['from_address'] = from_address

    dest_addr = msg.recipient.email_address

    try:
        ace.send(msg)
        # Log that the Activation Email has been sent to user without an exception
        log.info("Activation Email has been sent to User {user_email}".format(
            user_email=dest_addr
        ))
    except RecoverableChannelDeliveryError:
        log.info('Retrying sending email to user {dest_addr}, attempt # {attempt} of {max_attempts}'.format(
            dest_addr=dest_addr,
            attempt=retries,
            max_attempts=max_retries
        ))
        try:
            self.retry(countdown=settings.RETRY_ACTIVATION_EMAIL_TIMEOUT, max_retries=max_retries)
        except MaxRetriesExceededError:
            log.error(
                'Unable to send activation email to user from "%s" to "%s"',
                from_address,
                dest_addr,
                exc_info=True
            )
    except Exception:  # pylint: disable=bare-except
        log.exception(
            'Unable to send activation email to user from "%s" to "%s"',
            from_address,
            dest_addr,
            exc_info=True
        )
        raise Exception
示例#36
0
def send_activation_email(self, msg_string, from_address=None):
    """
    Sending an activation email to the user.
    """
    msg = Message.from_string(msg_string)

    max_retries = settings.RETRY_ACTIVATION_EMAIL_MAX_ATTEMPTS
    retries = self.request.retries

    if from_address is None:
        from_address = configuration_helpers.get_value(
            'ACTIVATION_EMAIL_FROM_ADDRESS') or (
                configuration_helpers.get_value('email_from_address',
                                                settings.DEFAULT_FROM_EMAIL))
    msg.options['from_address'] = from_address

    dest_addr = msg.recipient.email_address

    site = Site.objects.get_current()
    user = User.objects.get(id=msg.recipient.lms_user_id)

    try:
        with emulate_http_request(site=site, user=user):
            ace.send(msg)
    except RecoverableChannelDeliveryError:
        log.info(
            'Retrying sending email to user {dest_addr}, attempt # {attempt} of {max_attempts}'
            .format(dest_addr=dest_addr,
                    attempt=retries,
                    max_attempts=max_retries))
        try:
            self.retry(countdown=settings.RETRY_ACTIVATION_EMAIL_TIMEOUT,
                       max_retries=max_retries)
        except MaxRetriesExceededError:
            log.error(
                'Unable to send activation email to user from "%s" to "%s"',
                from_address,
                dest_addr,
                exc_info=True)
    except Exception:
        log.exception(
            'Unable to send activation email to user from "%s" to "%s"',
            from_address,
            dest_addr,
        )
        raise Exception  # lint-amnesty, pylint: disable=raise-missing-from
 def _contains_upsell_attribute(self, msg_attr):
     msg = Message.from_string(msg_attr)
     tmp = msg.context["show_upsell"]
     return msg.context["show_upsell"]
示例#38
0
 def _contains_upsell(self, message_str):
     message = Message.from_string(message_str)
     return message.context["show_upsell"]
示例#39
0
def _schedule_send(msg_str, site_id, delivery_config_var, log_prefix):
    if _is_delivery_enabled(site_id, delivery_config_var, log_prefix):
        msg = Message.from_string(msg_str)
        _annonate_send_task_for_monitoring(msg)
        LOG.debug('%s: Sending message = %s', log_prefix, msg_str)
        ace.send(msg)