コード例 #1
0
 def handle_noargs(self, **options):
     sailthru = Sailthru(FakeRequest())
     for count, product in enumerate(Product.objects.live(), 1):
         sailthru.update_product(product)
         if count % 100 == 0:
             print("Updated {} products".format(count), file=self.stdout)
     # Final count (don't repeat)
     if count % 100 != 0:
         print("Updated {} products".format(count), file=self.stdout)
コード例 #2
0
ファイル: views.py プロジェクト: codeadict/ecomarket
def sailthru_sync(request):
    """
    This page is used as the 'redirect action' for the SailThru unsubscribe
    page. We then pull the notification settings from SailThru and save them
    to our DB.
    """
    redirect_url = request.META.get('HTTP_REFERER')
    if redirect_url is None or 'link.ecomarket.com' not in redirect_url:
        redirect_url = 'http://www.ecomarket.com/'

    sailthru_id = request.GET.get('id', None)
    if sailthru_id is not None:
        try:
            sailthru_data = Sailthru(request).get_user_data(
                sailthru_id=sailthru_id)
        except SailthruError, e:
            logger.error(e, exc_info=True)
            raise  # TODO remove
        else:
            try:
                user = User.objects.get(email=sailthru_data['keys']['email'])
            except User.DoesNotExist:
                pass
            else:
                # The following variables are forced to enabled:
                #  - customer_reviews (notifications_customer_reviews)
                #  - orders (notifications_orders)
                #  - private_messages (notifications_private_messages)
                lists = sailthru_data.get('lists', {})
                user_notify = EmailNotification.objects.get(user=user)
                user_notify.blogs_you_might_like = contains(
                    lists, 'blogs_you_might_like')
                user_notify.follower_notifications = contains(
                    lists, 'follower_notifications')
                user_notify.product_discounts = contains(
                    lists, 'product_discounts')
                user_notify.products_you_might_like = contains(
                    lists, 'products_you_might_like')
                user_notify.site_updates_features = contains(
                    lists, 'site_updates_features')
                user_notify.stall_owner_tips = contains(
                    lists, 'stall_owner_tips')
                user_notify.save()
コード例 #3
0
 def send(self, template_name, user, subject):
     if user == self.commenter:
         return
     if (template_name, user.email) in self._sent_emails:
         # Refuse to send duplicates in the same session
         return
     self._sent_emails.add((template_name, user.email))
     return Sailthru(self.request).send_template(
         template_name,
         user.email,
         self.get_context(user))
コード例 #4
0
    def handle_noargs(self, **options):
        st = Sailthru(FakeRequest())
        for user in User.objects.exclude(orders=None):
            response = st.api.api_get('user', {'key':'email',
                                               'id':user.email,
                                               'fields':{'purchases':1}})
            try:
                data = st.check_response(response)
            except:
                continue

            if data['purchases'] is None:
                st_order_ids = []
            else:
                st_order_ids = []
                for purchase in data['purchases']:
                    for item in purchase['items']:
                        if 'vars' in item:
                            st_order_ids.append(item['vars']['order_id'])
            orders = (user.orders
                      .filter(Q(is_joomla_order=True) | ~Q(payment=None))
                      .exclude(id__in=st_order_ids))
            for order in orders:
                st.order_purchased(order)
コード例 #5
0
def notify_directors_of_paypal_error(request, info):
    context = {
        'pe_error_id':
        info['pe'].error_id,
        'pe_error':
        repr(info['pe']),
        'payment_attempt_id':
        info['payment_attempt'].id,
        'payment_id':
        info['payment'].id,
        'purchaser_email':
        request.user.email,
        'purchaser_id':
        request.user.id,
        'seller_notified':
        "Yes" if info['seller_notified'] else "No",
        'stall_user_email':
        info['cart_stall'].stall.user.email,
        'stall_phone_landline':
        info['cart_stall'].stall.phone_landline,
        'stall_phone_mobile':
        info['cart_stall'].stall.phone_mobile,
        "FNAME":
        info['payment_attempt'].cart_stall.stall.user.first_name,
        "ORDER_DATE":
        info['payment_attempt'].created.strftime("%d-%m-%Y"),
        "STALL_TITLE":
        info['payment_attempt'].cart_stall.stall.title,
        "STALL_URL":
        absolute_uri(
            reverse(
                "my_stall",
                kwargs={"slug":
                        info['payment_attempt'].cart_stall.stall.slug})),
        "CUSTOMER_USERNAME":
        info['payment_attempt'].cart_stall.cart.user.username,
        "CUSTOMER_PROFILE_URL":
        absolute_uri(
            reverse("public_profile",
                    kwargs={
                        "username":
                        info['payment_attempt'].cart_stall.cart.user.username
                    })),
    }
    to_email = '*****@*****.**'
    Sailthru(request).send_template('directors-paypal-error', to_email,
                                    context)
コード例 #6
0
ファイル: events.py プロジェクト: codeadict/ecomarket
 def __init__(self, request):
     self.request = request
     self.sailthru = Sailthru(request)
コード例 #7
0
ファイル: events.py プロジェクト: codeadict/ecomarket
class Events(object):
    """
    Trigger site-wide notifications for events which we want to keep track of.

    Usage:

    >>> Events(request).logged_in(request.user)
    """
    def __init__(self, request):
        self.request = request
        self.sailthru = Sailthru(request)

    def _sendmail(self, template_name, to_email, context):
        """
        Use SailThru to deliver a templated e-mail

        :param template_name: SailThru template name
        :param to_email: string E-mail address
        :param context: Dictionary of parameters for the template
        """
        assert isinstance(template_name, basestring)
        assert isinstance(to_email, basestring)
        assert type(context) == dict
        if self.sailthru.enabled():
            response = self.sailthru.send_template(template_name, to_email,
                                                   context)
            LOG.info("Sent e-mail to %s (SailThru send ID: %s)",
                     response['email'], response['send_id'])
            return response
        else:
            LOG.warn('Not sending template %s to %s because SailThru disabled',
                     template_name, to_email)
            return None

    def logged_in(self, user):
        """
        The user has logged in to the site

        :param user: User object
        """
        # Integration with sailthru
        if self.sailthru.enabled():
            self.sailthru.login(user)

        # Internal LifetimeTrack
        try:
            lt = LifetimeTrack.objects.get(user=user)
            merge_lifetime_track(self.request.lifetime_track, lt)
        except Exception as e:
            print "=-" * 34, e

        # Integration with mixpanel
        try:
            mixpanel_track(self.request, "logged in",
                           {"$last_seen": datetime.datetime.now()})
            mixpanel_engage(self.request, {'$add': {'Times Logged In': 1}})
            try:
                has_stall = user.stall is not None
            except:
                has_stall = False
            if has_stall:
                member_type = 'Regular'
            else:
                member_type = 'Stall Owner'
            mixpanel_engage(
                self.request, {
                    '$set': {
                        'last_login': datetime.datetime.now().isoformat(),
                        '$email': user.email,
                        '$username': user.username,
                        '$created': user.date_joined.isoformat(),
                        "$last_seen": datetime.datetime.now().isoformat(),
                        '$first_name': user.first_name,
                        '$last_name': user.last_name,
                        'gender': user.user_profile.get_gender_display(),
                        'mp_name_tag': user.get_full_name(),
                        'Member Type': member_type,
                    }
                })
        except:
            LOG.warn('Could not send login event to MixPanel', exc_info=True)

    def user_changed_email(self, user, old_email, new_email):
        """
        The user has changed their e-mail address

        :param user: User object
        :param old_email: Old e-mail address
        :param new_email: New e-mail address
        """
        from mailing_lists.models import MailingListSignup
        self.sailthru.change_email(old_email, new_email)
        old_mls = MailingListSignup.objects.filter(user=user)

        # Multiple mailing list signup objects for the same user
        # This shouldn't happen, but we can safely clean up.
        if len(old_mls) > 1:
            MailingListSignup.objects.filter(user=user).exclude(
                email_address=old_email).delete()
            old_mls = MailingListSignup.objects.filter(user=user)

        if MailingListSignup.objects.filter(
                email_address=new_email).count() != 0:
            LOG.warn(
                'Cant update MailingListSignup from %s to %s '
                'because MLS with new email already exists', old_email,
                new_email)
            return False

        if len(old_mls) == 1:
            old_mls[0].email_address = new_email
            old_mls[0].save()

    def cart_updated(self, cart):
        """
        Syncs contents of cart with SailThru and updates MixPanel properties
        for # of items in cart.

        :param cart: Cart object
        """
        try:
            self.sailthru.cart_updated(cart)
            mixpanel_engage(self.request,
                            {'$set': {
                                'Items in Cart': cart.num_items()
                            }})
        except:
            LOG.error("cart_updated failed", exc_info=True)

    def user_followed(self, follow):
        """
        :param follow: UserFollow object which has just been created
        """
        mixpanel_track(self.request, "Followed User",
                       {"followed_user": follow.target.username})

        mixpanel_engage(self.request, get_mixpanel_info(follow.user),
                        follow.user.id)

        mixpanel_engage(self.request,
                        get_mixpanel_info(follow.target, ignore_time=True),
                        follow.target.id)

        template_name = 'new-follower-notification'
        context = {
            'USER_USERNAME':
            follow.user.username,
            'USER_PROFILE_URL':
            absolute_uri(follow.user.get_profile().get_absolute_url()),
            'TARGET_USER_USERNAME':
            follow.target.username,
            'TARGET_USER_PROFILE_URL':
            absolute_uri(follow.target.get_profile().get_absolute_url())
        }
        print absolute_uri(follow.user.get_profile().get_absolute_url()), \
            absolute_uri(follow.target.get_profile().get_absolute_url())
        email_notification = follow.target.email_notification
        if email_notification.follower_notifications:
            self._sendmail(template_name, follow.target.email, context)
        else:
            LOG.info(
                "Email not send to %s, since follower_notifications is set to OFF",
                follow.target.email)

    def user_unfollowed(self, unfollow):
        """
        :param unfollow: UserFollow object which is about to be deleted
        """
        mixpanel_track(self.request, "Unfollowed User",
                       {"followed_user": unfollow.target.username})

        mixpanel_engage(self.request, get_mixpanel_info(unfollow.user),
                        unfollow.user.id)

        mixpanel_engage(self.request,
                        get_mixpanel_info(unfollow.target, ignore_time=True),
                        unfollow.target.id)

    def message_sent(self, msg):
        """
        A new message has been sent, notify the recipient via e-mail

        :param msg: Message object
        """
        if not msg.parent_msg:
            template_name = "new-message-received"
        else:
            template_name = "message-reply-received"

        # TODO: notification settings

        context = {
            "SENDER_USERNAME": msg.sender.username,
            "MESSAGE_SUBJECT": msg.subject,
            "MESSAGE_BODY": msg.body,
            'MESSAGE_VIEW_URL': absolute_uri(reverse('messaging_inbox'))
        }

        # send mail to hipchat
        template_context = Context({
            'request': self.request,
            'message': msg,
        })
        template = loader.get_template(
            "messaging/fragments/hipchat_message.html")
        output = template.render(template_context)

        send_to_hipchat(
            output,
            room_id=settings.HIPCHAT_MAIL_ROOM,
            notify=1,
        )

        mixpanel_track(self.request, "Sent Message", {})
        mixpanel_engage(self.request, {'$add': {'Messages Sent': 1}})

        self._sendmail(template_name, msg.recipient.email, context)

    def seller_paypal_error(self, payment_attempt):
        """
        The payment attempt failed because the Seller has an invalid Paypal
        address

        :param payment_attempt: PaymentAttempt object
        """
        try:
            stall = payment_attempt.cart_stall.stall
            stall_url = reverse("my_stall", kwargs={"slug": stall.slug})
            settings_url = reverse("account_email_notifications")
            context = {
                'stall': {
                    'title': stall.title,
                    'url': absolute_uri(stall_url),
                },
                'PAYPAL_SETTINGS_URL': absolute_uri(settings_url)
            }
            to_email = stall.user.email

            self._sendmail("seller-erroneous-paypal-account", to_email,
                           context)
        except:
            LOG.error('seller_paypal_error failed', exc_info=True)

    def _order_item_context(self, item):
        """
        XXX: move somewhere else?
        """
        product = item.product
        return {
            'qty': item.quantity,
            'title': product.title,
            'price': str(item.price),
            'total': str(item.price * item.quantity),
            'id': product.id,
            'url': absolute_uri(product.get_absolute_url())
        }

    def _order_context(self, order):
        """
        XXX: move somewhere else?
        """
        items = [
            self._order_item_context(line_item)
            for line_item in order.line_items.all()
        ]

        address = order.address

        delta = datetime.datetime.now().date() - order.created
        context = {
            'ORDER': {
                'items': items,
                'subtotal': str(order.subtotal()),
                'shipping': str(order.delivery_charge),
                'total': str(order.total()),
                'note': order.note,
                'address': {
                    'name': address.name,
                    'city': address.city,
                    'country': address.country.title,
                    'line1': address.line1,
                    'line2': address.line2,
                    'postal_code': address.postal_code,
                    'state': address.state,
                },
            },
            "FNAME":
            order.user.first_name,
            "ORDER_DATE":
            custom_strftime('{S} %B %Y', order.created),
            "CUSTOMER_FIRST_NAME":
            order.user.first_name,
            "CUSTOMER_FULL_NAME":
            order.user.get_profile().full_name,
            "CUSTOMER_USERNAME":
            order.user.username,
            "CUSTOMER_PROFILE_URL":
            absolute_uri(
                reverse("public_profile",
                        kwargs={"username": order.user.username})),
            "ORDER_ID":
            order.id,
            "INVOICE_URL":
            absolute_uri(reverse("invoice", kwargs={"order_id": order.id})),
            "STALL_TITLE":
            order.stall.title,
            "STALL_URL":
            absolute_uri(reverse("my_stall", kwargs={"slug":
                                                     order.stall.slug})),
            "STALL_OWNER_USERNAME":
            order.stall.user.username,
            'STALL_OWNER_URL':
            order.stall.user.get_profile().get_absolute_url(),
            "NUMBER_OF_DAYS_SINCE_ORDER":
            delta.days,
            "MESSAGE_CUSTOMER_URL":
            absolute_uri(
                reverse("messaging_compose_to",
                        kwargs={
                            "recipient": order.user.username,
                        })),
            "MESSAGE_STALL_URL":
            absolute_uri(
                reverse("messaging_compose_to",
                        kwargs={
                            "recipient": order.stall.user.username,
                        })),
            "UPDATE_PROFILE":
            absolute_uri(reverse("account_email_notifications")),
        }
        return context

    def _mixpanel_order_info(self, order):
        order_info = {
            "Order ID": order.id,
            "Order Date": order.created.isoformat(),
            "Total Order Value": str(order.total().amount),
            "Total Shipping Value": str(order.delivery_charge.amount),
            "No of Products": order.line_items.all().count(),
            "No of Items": order.num_items(),
            "Delivery Country": order.address.country.title
        }
        return order_info

    def order_reminder(self, order):
        """
        The seller needs to be reminded about an order.
        """
        days_map = {
            3: "3-day-dispatch-chase-up-stall-owner",
            7: "7-day-dispatch-chase-up-stall-owner",
            13: "13-day-dispatch-chase-up-stall-owner",
            14: "14-day-dispatch-chase-up-and-refund-warning",
        }
        days = (datetime.datetime.now().date() - order.created).days
        allowed_days = days_map.keys()
        if days not in allowed_days:
            return
        context = self._order_context(order)
        self._sendmail(days_map[days], order.stall.user.email, context)

    def order_refunded(self, order):
        """
        The seller has refunded an order
        """
        context = self._order_context(order)
        self._sendmail('order-refunded-to-customer', order.user.email, context)

        self._sendmail('order-refunded-to-stall-owner', order.stall.user.email,
                       context)

        try:
            # Track the Seller refunding the Order
            order_info = self._mixpanel_order_info(order)
            mixpanel_track(self.request, "Clicked Refund Button", order_info)
            mixpanel_engage(self.request, {'$add': {'Refunds Made': 1}})

            # Update the Buyers properties, reducing their GMV
            # XXX: we can't delete the transaction after it's refunded!!!
            #      adding the transaction needs to happen on Dispatch
            user = order.user
            stall = order.stall
            mixpanel_engage(None, {
                '$set': {
                    'Orders': user.orders.completed().count(),
                    'Total GMV to Date': str(
                        user.get_profile().total_gmv.amount),
                },
                '$ignore_time': True
            },
                            distinct_id=user.id)
        except:
            LOG.warning("Couldn't notify MixPanel of refund", exc_info=True)

    def order_dispatched(self, order):
        """
        The seller has marked an order as dispatched
        """
        try:
            context = self._order_context(order)
            self._sendmail('order-dispatched-to-customer', order.user.email,
                           context)
        except:
            LOG.warning("Couldn't send Order Dispatched email to Customer",
                        exc_info=True)

        try:
            order_info = self._mixpanel_order_info(order)
            mixpanel_track(self.request, 'Clicked Mark Order as Dispatched',
                           order_info)
            mixpanel_engage(self.request, {'$add': {
                'Orders Dispatched': 1
            }},
                            distinct_id=order.stall.user.id)
        except:
            LOG.warning("Couldn't update MixPanel after Order Dispatch",
                        exc_info=True)

    def order_placed(self, order):
        """
         1) Syncs cart with SailThru
         2) Sends 'Order Completed' e-mail to customer via Sailthru
         3) Syncs order information with MixPanel
        """
        # Technically the transaction isn't complete yet because the seller hasn't
        # marked the order as complete so could potentially refund the money.
        try:
            context = self._order_context(order)
            self._sendmail('order-placed-to-customer', order.user.email,
                           context)

            self._sendmail('order-placed-to-stall-owner',
                           order.stall.user.email, context)
        except:
            LOG.error("Events.order_placed failed to send email",
                      exc_info=True)

        # Send completed order to SailThru, this updates the users 'Total Revenue'
        try:
            self.sailthru.order_purchased(order)
        except:
            LOG.error("Events.order_placed failed to sync order with SailThru",
                      exc_info=True)

        # Sync user properties with MixPanel
        try:
            user = order.user
            mixpanel_engage(self.request, {
                '$set': {
                    'Orders': user.orders.completed().count(),
                    'Total GMV to Date': str(
                        user.get_profile().total_gmv.amount),
                }
            },
                            distinct_id=user.id)
            mixpanel_engage(self.request, {
                '$append': {
                    '$transactions': {
                        '$time': order.created.isoformat(),
                        '$amount': str(order.total().amount),
                        'Order ID': order.id
                    }
                }
            },
                            distinct_id=user.id)
            if self.request is not None:
                order_info = self._mixpanel_order_info(order)
                mixpanel_track(self.request, "Purchased Order", order_info)
        except:
            LOG.warning("Couldn't notify MixPanel of new order", exc_info=True)

    def forgot_password(self, user, token_generator=None):
        """
        Sends 'Forgot Username or Password?' with their username and a link
        to the password reset form.
        """
        from django.utils.http import int_to_base36
        if token_generator is None:
            from django.contrib.auth.tokens import default_token_generator
            token_generator = default_token_generator
        token = token_generator.make_token(user)
        uid = int_to_base36(user.id)
        ctx = dict(PASSWORD_RESET_URL=absolute_uri(
            reverse("password_reset_confirm",
                    kwargs={
                        "uidb36": uid,
                        "token": token
                    })))
        self._sendmail('forgot-username-or-password', user.email, ctx)

    def stall_opened(self, stall):
        """
        A new stall has been opened, welcome the user to the site and give them
        information about how to add products & optimize their stuff.
        """
        user = stall.user
        context = {
            "STALL_URL":
            absolute_uri(reverse("my_stall", kwargs={
                "slug": stall.slug,
            }))
        }
        self._sendmail("stall-owner-welcome", user.email, context)

    def user_signup(self, user, requires_activation=True):
        """
        Welcomes the user to the site
        Includes URL for them to activate their account / verify their e-mail.
        """
        if requires_activation:
            user_profile = user.user_profile
            verify_url = absolute_uri(
                reverse("verify", args=[user_profile.activation_key]))
            ctx = dict(ACTIVATION_URL=verify_url)
            self._sendmail('regular-user-welcome-email', user.email, ctx)

        profile = user.get_profile()

        # Fix up their MailingLists objects
        from mailing_lists.models import MailingListSignup
        mls = MailingListSignup.objects.create_from_user(user)
        mls.marketing_optin = profile.send_newsletters
        if self.request is not None:
            mls.set_ip_address(self.request)
        mls.save()

        # Apply defaults to EmailNotification preferences
        email_notification = user.email_notification
        email_notification.site_updates_features = profile.send_newsletters
        email_notification.stall_owner_tips = True
        email_notification.product_inspirations = profile.send_newsletters
        email_notification.blogs_you_might_like = profile.send_newsletters
        email_notification.save()

        # Integration with sailthru
        if self.sailthru.enabled():
            self.sailthru.signup(user=user)

    def newsletter_signup(self, email):
        """
        This is a newsletter signup that is available from the homepage modal,
        and at the bottom of the blog.
        """
        # Integration with sailthru
        # XXX: we should be doing the mailinglistsignup thing here too!!!
        if self.sailthru.enabled():
            self.sailthru.signup(email=email)

    def stall_stockcheck(self, stall):
        user = stall.user
        context = {
            "STALL_URL":
            absolute_uri(reverse("my_stall", kwargs={
                "slug": stall.slug,
            })),
            "DAYS_LEFT":
            stall.days_to_next_stockcheck
        }
        self._sendmail("stock-check-reminder-1", user.email, context)

    def stall_stockcheck_urgent(self, stall):
        user = stall.user
        context = {
            "STALL_URL":
            absolute_uri(reverse("my_stall", kwargs={
                "slug": stall.slug,
            })),
            "DAYS_LEFT":
            stall.days_to_next_stockcheck
        }
        self._sendmail("stock-check-reminder-2", user.email, context)

    def stall_suspended(self, stall):
        user = stall.user
        context = {
            "STALL_URL":
            absolute_uri(reverse("my_stall", kwargs={
                "slug": stall.slug,
            }))
        }
        self._sendmail("stock-check-suspension-notice", user.email, context)