def test_sending_a_order_related_messages(self):
        email = '*****@*****.**'
        user = User.objects.create_user('testuser', email,
                                        'somesimplepassword')

        order_number = '12345'
        order = create_order(number=order_number, user=user)
        et = CommunicationEventType.objects.create(code="ORDER_PLACED",
                                                   name="Order Placed",
                                                   category="Order related")

        messages = et.get_messages({
            'order': order,
            'lines': order.lines.all()
        })

        self.assertIn(order_number, messages['body'])
        self.assertIn(order_number, messages['html'])

        dispatcher = Dispatcher()
        dispatcher.dispatch_order_messages(order, messages, et)

        self.assertEquals(len(mail.outbox), 1)

        message = mail.outbox[0]
        self.assertIn(order_number, message.body)
Example #2
0
 def test_dispatcher_uses_email_connection(self):
     connection = mail.get_connection()
     disp = Dispatcher(mail_connection=connection)
     disp.dispatch_direct_messages('*****@*****.**', {
         'subject': 'Test',
         'body': 'This is a test.',
         'html': '',
     })
     self.assertEqual(len(mail.outbox), 1)
Example #3
0
 def _dispatch_user_messages(self, user, event_code, ctx, subject):
     msgs = CommunicationEventType.objects.get_and_render(
         code=event_code, context=ctx)
     Dispatcher().dispatch_user_messages(user, msgs)
     self.assertEqual(len(mail.outbox), 1)
     self.assertEqual(mail.outbox[0].subject, subject)
     self.assertEqual(Email.objects.count(), 1)
     email = Email.objects.last()
     self.assertEqual(email.user.id, user.id)
     self.assertEqual(email.email, '*****@*****.**')
Example #4
0
    def _dispatch_order_messages(self, order_number, order, email=None):
        et = CommunicationEventType.objects.create(code="ORDER_PLACED",
                                                   name="Order Placed",
                                                   category=CommunicationEventType.ORDER_RELATED)

        messages = et.get_messages({
            'order': order,
            'lines': order.lines.all()
        })

        self.assertIn(order_number, messages['body'])
        self.assertIn(order_number, messages['html'])

        dispatcher = Dispatcher()
        dispatcher.dispatch_order_messages(order, messages, et)

        self.assertEqual(CommunicationEvent.objects.filter(order=order, event_type=et).count(), 1)

        self.assertEqual(len(mail.outbox), 1)

        message = mail.outbox[0]
        self.assertIn(order_number, message.body)

        # test sending messages to emails without account and text body
        messages['body'] = ''
        dispatcher.dispatch_direct_messages(email, messages)
        self.assertEqual(len(mail.outbox), 2)
Example #5
0
def send_alert_confirmation(alert):
    """
    Send an alert confirmation email.
    """
    ctx = {
        'alert': alert,
        'site': Site.objects.get_current(),
    }

    # For backwards compability, we check if the old (non-communication-event)
    # templates exist, and use them if they do.
    # This will be removed in Oscar 2.0
    try:
        subject_tpl = loader.get_template('customer/alerts/emails/'
                                          'confirmation_subject.txt')
        body_tpl = loader.get_template('customer/alerts/emails/'
                                       'confirmation_body.txt')
        warnings.warn(
            "Product alert notifications now use the CommunicationEvent. "
            "Move '{}' to '{}', and '{}' to '{}'".format(
                'customer/alerts/emails/confirmation_subject.txt',
                'customer/emails/commtype_product_alert_confirmation_subject.txt',
                'customer/alerts/emails/confirmation_body.txt',
                'customer/emails/commtype_product_alert_confirmation_body.txt',
            ),
            category=RemovedInOscar20Warning,
            stacklevel=2)

        messages = {
            'subject': subject_tpl.render(ctx).strip(),
            'body': body_tpl.render(ctx),
            'html': '',
            'sms': '',
        }
    except TemplateDoesNotExist:
        code = 'PRODUCT_ALERT_CONFIRMATION'
        messages = CommunicationEventType.objects.get_and_render(code, ctx)

    if messages and messages['body']:
        Dispatcher().dispatch_direct_messages(alert.email, messages)
Example #6
0
 def test_sending_user_related_message(self):
     email = '*****@*****.**'
     user = User.objects.create_user('testuser', email,
                                     'somesimplepassword')
     CommunicationEventType.objects.create(
         code='EMAIL_CHANGED',
         name='Email Changed',
         category=CommunicationEventType.USER_RELATED)
     ctx = {
         'user': user,
         'site': SiteFactory(name='Test Site'),
         'reset_url': get_password_reset_url(user),
         'new_email': '*****@*****.**',
     }
     msgs = CommunicationEventType.objects.get_and_render(
         code='EMAIL_CHANGED', context=ctx)
     Dispatcher().dispatch_user_messages(user, msgs)
     self.assertEqual(len(mail.outbox), 1)
     self.assertEquals(mail.outbox[0].subject,
                       'Your email address has changed at Test Site.')
     self.assertEquals(Email.objects.count(), 1)
     email = Email.objects.last()
     self.assertEquals(email.user.id, user.id)
     self.assertEquals(email.email, '*****@*****.**')
Example #7
0
def send_product_alerts(product):  # noqa C901 too complex
    """
    Check for notifications for this product and send email to users
    if the product is back in stock. Add a little 'hurry' note if the
    amount of in-stock items is less then the number of notifications.
    """
    stockrecords = product.stockrecords.all()
    num_stockrecords = len(stockrecords)
    if not num_stockrecords:
        return

    logger.info("Sending alerts for '%s'", product)
    alerts = ProductAlert.objects.filter(
        product_id__in=(product.id, product.parent_id),
        status=ProductAlert.ACTIVE,
    )

    # Determine 'hurry mode'
    if num_stockrecords == 1:
        num_in_stock = stockrecords[0].num_in_stock
    else:
        result = stockrecords.aggregate(max_in_stock=Max('num_in_stock'))
        num_in_stock = result['max_in_stock']

    # hurry_mode is false if num_in_stock is None
    hurry_mode = num_in_stock is not None and alerts.count() > num_in_stock

    # For backwards compability, we check if the old (non-communication-event)
    # templates exist, and use them if they do.
    # This will be removed in Oscar 2.0
    try:
        email_subject_tpl = loader.get_template('customer/alerts/emails/'
                                                'alert_subject.txt')
        email_body_tpl = loader.get_template('customer/alerts/emails/'
                                             'alert_body.txt')

        use_deprecated_templates = True
        warnings.warn(
            "Product alert notifications now use the CommunicationEvent. "
            "Move '{}' to '{}', and '{}' to '{}'".format(
                'customer/alerts/emails/alert_subject.txt',
                'customer/emails/commtype_product_alert_subject.txt',
                'customer/alerts/emails/alert_body.txt',
                'customer/emails/commtype_product_alert_body.txt',
            ),
            category=RemovedInOscar20Warning,
            stacklevel=2)

    except TemplateDoesNotExist:
        code = 'PRODUCT_ALERT'
        try:
            event_type = CommunicationEventType.objects.get(code=code)
        except CommunicationEventType.DoesNotExist:
            event_type = CommunicationEventType.objects.model(code=code)
        use_deprecated_templates = False

    messages_to_send = []
    user_messages_to_send = []
    num_notifications = 0
    selector = Selector()
    for alert in alerts:
        # Check if the product is available to this user
        strategy = selector.strategy(user=alert.user)
        data = strategy.fetch_for_product(product)
        if not data.availability.is_available_to_buy:
            continue

        ctx = {
            'alert': alert,
            'site': Site.objects.get_current(),
            'hurry': hurry_mode,
        }
        if alert.user:
            # Send a site notification
            num_notifications += 1
            subj_tpl = loader.get_template(
                'customer/alerts/message_subject.html')
            message_tpl = loader.get_template('customer/alerts/message.html')
            services.notify_user(alert.user,
                                 subj_tpl.render(ctx).strip(),
                                 body=message_tpl.render(ctx).strip())

        # Build message and add to list
        if use_deprecated_templates:
            messages = {
                'subject': email_subject_tpl.render(ctx).strip(),
                'body': email_body_tpl.render(ctx),
                'html': '',
                'sms': '',
            }
        else:
            messages = event_type.get_messages(ctx)

        if messages and messages['body']:
            if alert.user:
                user_messages_to_send.append((alert.user, messages))
            else:
                messages_to_send.append((alert.get_email_address(), messages))
        alert.close()

    # Send all messages using one SMTP connection to avoid opening lots of them
    if messages_to_send or user_messages_to_send:
        connection = mail.get_connection()
        connection.open()
        disp = Dispatcher(mail_connection=connection)
        for message in messages_to_send:
            disp.dispatch_direct_messages(*message)
        for message in user_messages_to_send:
            disp.dispatch_user_messages(*message)
        connection.close()

    logger.info("Sent %d notifications and %d messages", num_notifications,
                len(messages_to_send) + len(user_messages_to_send))
Example #8
0
def send_product_alerts(product):   # noqa C901 too complex
    """
    Check for notifications for this product and send email to users
    if the product is back in stock. Add a little 'hurry' note if the
    amount of in-stock items is less then the number of notifications.
    """
    stockrecords = product.stockrecords.all()
    num_stockrecords = len(stockrecords)
    if not num_stockrecords:
        return

    logger.info("Sending alerts for '%s'", product)
    alerts = ProductAlert.objects.filter(
        product_id__in=(product.id, product.parent_id),
        status=ProductAlert.ACTIVE,
    )

    # Determine 'hurry mode'
    if num_stockrecords == 1:
        num_in_stock = stockrecords[0].num_in_stock
    else:
        result = stockrecords.aggregate(max_in_stock=Max('num_in_stock'))
        num_in_stock = result['max_in_stock']

    # hurry_mode is false if num_in_stock is None
    hurry_mode = num_in_stock is not None and alerts.count() > num_in_stock

    # For backwards compability, we check if the old (non-communication-event)
    # templates exist, and use them if they do.
    # This will be removed in Oscar 2.0
    try:
        email_subject_tpl = loader.get_template('customer/alerts/emails/'
                                                'alert_subject.txt')
        email_body_tpl = loader.get_template('customer/alerts/emails/'
                                             'alert_body.txt')

        use_deprecated_templates = True
        warnings.warn(
            "Product alert notifications now use the CommunicationEvent. "
            "Move '{}' to '{}', and '{}' to '{}'".format(
                'customer/alerts/emails/alert_subject.txt',
                'customer/emails/commtype_product_alert_subject.txt',
                'customer/alerts/emails/alert_body.txt',
                'customer/emails/commtype_product_alert_body.txt',
            ),
            category=RemovedInOscar20Warning, stacklevel=2
        )

    except TemplateDoesNotExist:
        code = 'PRODUCT_ALERT'
        try:
            event_type = CommunicationEventType.objects.get(code=code)
        except CommunicationEventType.DoesNotExist:
            event_type = CommunicationEventType.objects.model(code=code)
        use_deprecated_templates = False

    messages_to_send = []
    user_messages_to_send = []
    num_notifications = 0
    selector = Selector()
    for alert in alerts:
        # Check if the product is available to this user
        strategy = selector.strategy(user=alert.user)
        data = strategy.fetch_for_product(product)
        if not data.availability.is_available_to_buy:
            continue

        ctx = {
            'alert': alert,
            'site': Site.objects.get_current(),
            'hurry': hurry_mode,
        }
        if alert.user:
            # Send a site notification
            num_notifications += 1
            subj_tpl = loader.get_template('customer/alerts/message_subject.html')
            message_tpl = loader.get_template('customer/alerts/message.html')
            services.notify_user(
                alert.user,
                subj_tpl.render(ctx).strip(),
                body=message_tpl.render(ctx).strip()
            )

        # Build message and add to list
        if use_deprecated_templates:
            messages = {
                'subject': email_subject_tpl.render(ctx).strip(),
                'body': email_body_tpl.render(ctx),
                'html': '',
                'sms': '',
            }
        else:
            messages = event_type.get_messages(ctx)

        if messages and messages['body']:
            if alert.user:
                user_messages_to_send.append(
                    (alert.user, messages)
                )
            else:
                messages_to_send.append(
                    (alert.get_email_address(), messages)
                )
        alert.close()

    # Send all messages using one SMTP connection to avoid opening lots of them
    if messages_to_send or user_messages_to_send:
        connection = mail.get_connection()
        connection.open()
        disp = Dispatcher(mail_connection=connection)
        for message in messages_to_send:
            disp.dispatch_direct_messages(*message)
        for message in user_messages_to_send:
            disp.dispatch_user_messages(*message)
        connection.close()

    logger.info("Sent %d notifications and %d messages", num_notifications,
                len(messages_to_send) + len(user_messages_to_send))