def handle(self, *args, **options):

        # Mozillians has some settings that must be set, or it'll just skip talking to Basket.
        # The tasks module looks these up at import time, and might get BASKET_API_KEY
        # from the environment rather than settings if it's there, so look directly at
        # what values the tasks module ended up with.
        required_settings = ['BASKET_API_KEY', 'BASKET_NDA_NEWSLETTER',
                             'BASKET_URL', 'BASKET_VOUCHED_NEWSLETTER']
        if not all([getattr(tasks, setting, False) for setting in required_settings]):
            # At least one is missing. Show what's set and what's missing:
            for setting in required_settings:
                val = getattr(tasks, setting, False)
                if not val:
                    self.stdout.write('** %s is not set and must be **\n' % setting)
                else:
                    self.stdout.write('%s=%s\n' % (setting, val))
            raise CommandError('ERROR: Basket is not enabled with current settings')

        email = '*****@*****.**'

        try:
            lookup_user(email=email)
        except BasketException as exception:
            if exception.code != BASKET_UNKNOWN_EMAIL:
                raise CommandError('ERROR: Error querying basket: %s' % exception)

        # basket.lookup_user will have queried Exact Target for the user's subscriptions,
        # or whether the user exists, and failed if it couldn't get to Exact Target.  Since we got
        # this far, we know things are okay all the way through to Exact Target.
        self.stdout.write('Basket is working okay.\n')
Exemple #2
0
 def test_lookup_user_email(self, mock_request):
     """Calling lookup_user with email and api key should succeed."""
     api_key = 'There is only XUL!'
     email = '*****@*****.**'
     lookup_user(email=email, api_key=api_key)
     mock_request.assert_called_with('get', 'lookup-user',
                                     params={'email': email},
                                     headers={'x-api-key': api_key})
Exemple #3
0
 def test_lookup_user_email(self, mock_request):
     """Calling lookup_user with email and api key should succeed."""
     api_key = 'There is only XUL!'
     email = '*****@*****.**'
     lookup_user(email=email, api_key=api_key)
     mock_request.assert_called_with('get', 'lookup-user',
                                     params={'email': email},
                                     headers={'x-api-key': api_key})
Exemple #4
0
 def test_lookup_user_email_setting(self, mock_request):
     """Calling lookup_user with email and api key setting should succeed."""
     api_key = 'There is only XUL!'
     email = '*****@*****.**'
     with patch('basket.base.BASKET_API_KEY', api_key):
         lookup_user(email=email)
     mock_request.assert_called_with('get', 'lookup-user',
                                     params={'email': email},
                                     headers={'x-api-key': api_key})
Exemple #5
0
 def test_lookup_user_email_setting(self, mock_request):
     """Calling lookup_user with email and api key setting should succeed."""
     api_key = 'There is only XUL!'
     email = '*****@*****.**'
     with patch('basket.base.BASKET_API_KEY', api_key):
         lookup_user(email=email)
     mock_request.assert_called_with('get', 'lookup-user',
                                     params={'email': email},
                                     headers={'x-api-key': api_key})
Exemple #6
0
def unsubscribe_from_basket_task(email, basket_token):
    """Remove from Basket Task.

    This task unsubscribes a user from the Mozillians newsletter.
    The task retries on failure at most BASKET_TASK_MAX_RETRIES times
    and if it finally doesn't complete successfully, it emails the
    settings.BASKET_MANAGERS with details.

    """
    # IMPLEMENTATION NOTE:
    #
    # This task might run AFTER the User has been deleted, so it can't
    # look anything up about the user locally. It has to make do
    # with the email and token passed in.

    if not BASKET_ENABLED:
        return

    try:
        if not basket_token:
            # We don't have this user's token yet, and we need it to
            # unsubscribe.  Ask basket for it
            basket_token = basket.lookup_user(email=email)['token']

        basket.unsubscribe(basket_token,
                           email,
                           newsletters=settings.BASKET_NEWSLETTER)
    except (requests.exceptions.RequestException,
            basket.BasketException) as exception:
        try:
            unsubscribe_from_basket_task.retry()
        except (MaxRetriesExceededError, basket.BasketException):
            _email_basket_managers('unsubscribe', email, exception.message)
Exemple #7
0
def update_basket_token_task(instance_id):
    """Update basket token task

    This task looks up user email in basket and deletes the current basket_token
    if email doesn't exist in basket or updates it if it exists but it's not the same.

    """
    from models import UserProfile
    try:
        instance = UserProfile.objects.get(pk=instance_id)
    except UserProfile.DoesNotExist:
        instance = None

    if not BASKET_ENABLED or not instance or not waffle.switch_is_active(
            'BASKET_SWITCH_ENABLED'):
        return

    try:
        response = basket.lookup_user(email=instance.email)
        token = response['token']

    except basket.BasketException as exception:
        if exception.code == basket.errors.BASKET_UNKNOWN_EMAIL:
            UserProfile.objects.filter(pk=instance.pk).update(basket_token='')
            return
        update_basket_token_task.retry()
    except MaxRetriesExceededError:
        _email_basket_managers('update token', instance.email,
                               exception.message)
    except requests.exceptions.RequestException:
        update_basket_token_task.retry()

    if not token:
        token = ''
    UserProfile.objects.filter(pk=instance.pk).update(basket_token=token)
Exemple #8
0
def unsubscribe_from_basket_task(email, basket_token):
    """Remove from Basket Task.

    This task unsubscribes a user from the Mozillians newsletter.
    The task retries on failure at most BASKET_TASK_MAX_RETRIES times
    and if it finally doesn't complete successfully, it emails the
    settings.BASKET_MANAGERS with details.

    """
    # IMPLEMENTATION NOTE:
    #
    # This task might run AFTER the User has been deleted, so it can't
    # look anything up about the user locally. It has to make do
    # with the email and token passed in.

    if not BASKET_ENABLED:
        return

    try:
        if not basket_token:
            # We don't have this user's token yet, and we need it to
            # unsubscribe.  Ask basket for it
            basket_token = basket.lookup_user(email=email)['token']

        basket.unsubscribe(basket_token, email,
                           newsletters=settings.BASKET_NEWSLETTER)
    except (requests.exceptions.RequestException,
            basket.BasketException) as exception:
        try:
            unsubscribe_from_basket_task.retry()
        except (MaxRetriesExceededError, basket.BasketException):
            _email_basket_managers('unsubscribe', email, exception.message)
Exemple #9
0
def update_basket_token_task(instance_id):
    """Update basket token task

    This task looks up user email in basket and deletes the current basket_token
    if email doesn't exist in basket or updates it if it exists but it's not the same.

    """
    from models import UserProfile
    try:
        instance = UserProfile.objects.get(pk=instance_id)
    except UserProfile.DoesNotExist:
        instance = None

    if not BASKET_ENABLED or not instance or not waffle.switch_is_active('BASKET_SWITCH_ENABLED'):
        return

    try:
        token = basket.lookup_user(email=instance.email)['token']
        UserProfile.objects.filter(pk=instance.pk).update(basket_token=token)

    except basket.BasketException as exception:
        if exception.code == basket.errors.BASKET_UNKNOWN_EMAIL:
            UserProfile.objects.filter(pk=instance.pk).update(basket_token='')
            return
        update_basket_token_task.retry()
    except MaxRetriesExceededError:
        _email_basket_managers('update token', instance.email, exception.message)
    except requests.exceptions.RequestException:
        update_basket_token_task.retry()
Exemple #10
0
def get_subscription_details(email):
    subscription_details = None
    try:
        subscription_details = basket.lookup_user(email=email,
                                        api_key=constance.config.BASKET_API_KEY)
    except BasketException, e:
        if e.code == BASKET_UNKNOWN_EMAIL:
            # pass - unknown email is just a new subscriber
            pass
Exemple #11
0
def get_subscription_details(email):
    subscription_details = None
    try:
        subscription_details = basket.lookup_user(
            email=email, api_key=constance.config.BASKET_API_KEY)
    except BasketException, e:
        if e.code == BASKET_UNKNOWN_EMAIL:
            # pass - unknown email is just a new subscriber
            pass
Exemple #12
0
def lookup_user_task(self, email):
    """Task responsible for getting information about a user in basket."""

    # We need to return always a dictionary for the next task
    result = {}

    try:
        result = basket.lookup_user(email=email)
    except MaxRetriesExceededError as exc:
        raise exc
    except basket.BasketException as exc:
        if not exc[0] == u'User not found':
            raise self.retry(exc=exc)
        result = exc.result
    return result
def lookup_user_task(self, email):
    """Task responsible for getting information about a user in basket."""

    # We need to return always a dictionary for the next task
    result = {}

    try:
        result = basket.lookup_user(email=email)
    except MaxRetriesExceededError as exc:
        raise exc
    except basket.BasketException as exc:
        if not exc[0] == u'User not found':
            raise self.retry(exc=exc)
        result = exc.result
    return result
Exemple #14
0
    def lookup_basket_token(self):
        """
        Query Basket for this user's token.  If Basket doesn't find the user,
        returns None. If Basket does find the token, returns it. Otherwise,
        there must have been some error from the network or basket, and this
        method just lets that exception propagate so the caller can decide how
        best to handle it.

        (Does not update the token field on the UserProfile.)
        """
        try:
            result = basket.lookup_user(email=self.user.email)
        except basket.BasketException as exception:
            if exception.code == basket.errors.BASKET_UNKNOWN_EMAIL:
                return None
            raise
        return result['token']
Exemple #15
0
    def lookup_basket_token(self):
        """
        Query Basket for this user's token.  If Basket doesn't find the user,
        returns None. If Basket does find the token, returns it. Otherwise,
        there must have been some error from the network or basket, and this
        method just lets that exception propagate so the caller can decide how
        best to handle it.

        (Does not update the token field on the UserProfile.)
        """
        try:
            result = basket.lookup_user(email=self.user.email)
        except basket.BasketException as exception:
            if exception.code == basket.errors.BASKET_UNKNOWN_EMAIL:
                return None
            raise
        return result['token']
Exemple #16
0
def sync_user_with_basket(user):
    """Syncronize a user with basket.

    Returns the user data in case of a successful sync.
    Returns `None` in case of an unsuccessful sync. This can happen
    if the user does not exist in basket yet.

    This raises an exception all other errors.
    """
    try:
        data = basket.lookup_user(user.email)
        user.update(basket_token=data['token'])
        return data
    except Exception as exc:
        acceptable_errors = (basket.errors.BASKET_INVALID_EMAIL,
                             basket.errors.BASKET_UNKNOWN_EMAIL)

        if getattr(exc, 'code', None) in acceptable_errors:
            return None
        else:
            raise
Exemple #17
0
def sync_user_with_basket(user):
    """Syncronize a user with basket.

    Returns the user data in case of a successful sync.
    Returns `None` in case of an unsuccessful sync. This can happen
    if the user does not exist in basket yet.

    This raises an exception all other errors.
    """
    try:
        data = basket.lookup_user(user.email)
        user.update(basket_token=data['token'])
        return data
    except Exception as exc:
        acceptable_errors = (
            basket.errors.BASKET_INVALID_EMAIL,
            basket.errors.BASKET_UNKNOWN_EMAIL)

        if getattr(exc, 'code', None) in acceptable_errors:
            return None
        else:
            raise
Exemple #18
0
    def test_lookup_user_no_api_key(self, mock_request):
        """Calling lookup_user with email and no api key raises an exception."""
        with self.assertRaises(BasketException):
            lookup_user(email='*****@*****.**')

        self.assertFalse(mock_request.called)
Exemple #19
0
def update_basket_task(instance_id):
    """Update Basket Task.

    This task subscribes a user to Basket, if not already subscribed
    and then updates his data on the Phonebook DataExtension. The task
    retries on failure at most BASKET_TASK_MAX_RETRIES times and if it
    finally doesn't complete successfully, it emails the
    settings.BASKET_MANAGERS with details.

    """
    # This task is triggered by a post-save signal on UserProfile, so
    # we can't save() on UserProfile again while in here - if we were
    # running with CELERY_EAGER, we'd enter an infinite recursion until
    # Python died.

    from models import UserProfile

    instance = UserProfile.objects.get(pk=instance_id)

    if not BASKET_ENABLED or not instance.is_vouched:
        return

    email = instance.user.email
    token = instance.basket_token

    if not token:
        # no token yet, they're probably not subscribed, so subscribe them.
        # pass sync='Y' so we wait for it to complete and get back the token.
        try:
            retval = basket.subscribe(email, [settings.BASKET_NEWSLETTER], sync="Y", trigger_welcome="N")
        except (requests.exceptions.RequestException, basket.BasketException) as exception:
            try:
                update_basket_task.retry()
            except (MaxRetriesExceededError, basket.BasketException):
                _email_basket_managers("subscribe", instance.user.email, exception.message)
            return
        # Remember the token
        instance.basket_token = retval["token"]
        token = retval["token"]
        # Don't call .save() on a userprofile from here, it would invoke us again
        # via the post-save signal, which would be pointless.
        UserProfile.objects.filter(pk=instance.pk).update(basket_token=token)
    else:
        # They were already subscribed. See what email address they
        # have in exact target. If it has changed, we'll need to
        # unsubscribe the old address and subscribe the new one,
        # and save the new token.
        # This'll also return their subscriptions, so we can transfer them
        # to the new address if we need to.
        try:
            result = basket.lookup_user(token=token)
        except basket.BasketException as exception:
            try:
                update_basket_task.retry()
            except (MaxRetriesExceededError, basket.BasketException):
                msg = exception.message
                _email_basket_managers("update_phonebook", token, msg)
            return
        old_email = result["email"]
        if old_email != email:
            try:
                # We do the new subscribe first, then the unsubscribe, so we don't
                # risk losing their subscriptions if the subscribe fails.
                # Subscribe to all the same newsletters.
                # Pass sync='Y' so we get back the new token right away
                subscribe_result = basket.subscribe(email, [settings.BASKET_NEWSLETTER], sync="Y", trigger_welcome="N")
                # unsub all from the old token
                basket.unsubscribe(token=token, email=old_email, newsletters=[settings.BASKET_NEWSLETTER], optout="Y")
            except (requests.exceptions.RequestException, basket.BasketException) as exception:
                try:
                    update_basket_task.retry()
                except (MaxRetriesExceededError, basket.BasketException):
                    _email_basket_managers("subscribe", email, exception.message)
                return
            # FIXME: We should also remove their previous phonebook record from Exact Target, but
            # basket doesn't have a custom API to do that. (basket never deletes anything.)

            # That was all successful. Update the token.
            instance.basket_token = subscribe_result["token"]
            token = subscribe_result["token"]
            # Don't call .save() on a userprofile from here, it would invoke us again
            # via the post-save signal, which would be pointless.
            UserProfile.objects.filter(pk=instance.pk).update(basket_token=token)

    GroupMembership = get_model("groups", "GroupMembership")
    Group = get_model("groups", "Group")
    data = {}
    # What groups is the user in?
    user_group_pks = instance.groups.filter(groupmembership__status=GroupMembership.MEMBER).values_list("pk", flat=True)
    for group in Group.objects.filter(functional_area=True):
        name = group.name.upper().replace(" ", "_")
        data[name] = "Y" if group.id in user_group_pks else "N"

    # User location if known
    if instance.geo_country:
        data["country"] = instance.geo_country.code
    if instance.geo_city:
        data["city"] = instance.geo_city.name

    # We have a token, proceed with the update
    try:
        basket.request("post", "custom_update_phonebook", token=token, data=data)
    except (requests.exceptions.RequestException, basket.BasketException) as exception:
        try:
            update_basket_task.retry()
        except (MaxRetriesExceededError, basket.BasketException):
            _email_basket_managers("update_phonebook", email, exception.message)
Exemple #20
0
    def test_lookup_user_no_args(self, mock_request):
        """Calling lookup_user with no email or token raises an exception."""
        with self.assertRaises(BasketException):
            lookup_user()

        self.assertFalse(mock_request.called)
Exemple #21
0
    def test_lookup_user_no_api_key(self, mock_request):
        """Calling lookup_user with email and no api key raises an exception."""
        with self.assertRaises(BasketException):
            lookup_user(email='*****@*****.**')

        self.assertFalse(mock_request.called)
Exemple #22
0
def update_basket_task(instance_id, newsletters=[]):
    """Update Basket Task.

    This task subscribes a user to Basket, if not already subscribed
    and then updates his data on the Phonebook DataExtension. The task
    retries on failure at most BASKET_TASK_MAX_RETRIES times and if it
    finally doesn't complete successfully, it emails the
    settings.BASKET_MANAGERS with details.

    """
    # This task is triggered by a post-save signal on UserProfile, so
    # we can't save() on UserProfile again while in here - if we were
    # running with CELERY_EAGER, we'd enter an infinite recursion until
    # Python died.

    from models import UserProfile
    try:
        instance = UserProfile.objects.get(pk=instance_id)
    except UserProfile.DoesNotExist:
        instance = None

    if (not BASKET_ENABLED or not instance or not newsletters or
            not waffle.switch_is_active('BASKET_SWITCH_ENABLED')):
        return

    email = instance.user.email
    token = instance.basket_token
    newsletters_to_subscribe = []

    if token:
        # They were already subscribed. See what email address they
        # have in exact target. If it has changed, we'll need to
        # unsubscribe the old address and subscribe the new one,
        # and save the new token.
        # This'll also return their subscriptions, so we can transfer them
        # to the new address if we need to.
        try:
            result = basket.lookup_user(token=token)
        except basket.BasketException as exception:
            try:
                update_basket_task.retry()
            except (MaxRetriesExceededError, basket.BasketException):
                msg = exception.message
                _email_basket_managers('update_phonebook', token, msg)
            return

        # Check if the users needs to be subscribed to additional newsletters.
        newsletters_to_subscribe = [nl for nl in newsletters if nl not in result['newsletters']]

        old_email = result['email']
        if old_email != email:
            try:
                # We do the new subscribe first, then the unsubscribe, so we don't
                # risk losing their subscriptions if the subscribe fails.
                # Subscribe to all the same newsletters.
                # Pass sync='Y' so we get back the new token right away
                subscribe_result = basket.subscribe(
                    email,
                    result['newsletters'],
                    sync='Y',
                    trigger_welcome='N',
                )
                # unsubscribe all from the old token
                basket.unsubscribe(token=token, email=old_email, optout=True)
            except (requests.exceptions.RequestException,
                    basket.BasketException) as exception:
                try:
                    update_basket_task.retry()
                except (MaxRetriesExceededError, basket.BasketException):
                    _email_basket_managers('subscribe', email, exception.message)
                return

            # That was all successful. Update the token.
            token = subscribe_result['token']
            # Don't call .save() on a userprofile from here, it would invoke us again
            # via the post-save signal, which would be pointless.
            UserProfile.objects.filter(pk=instance.pk).update(basket_token=token)

    if not token or newsletters_to_subscribe:
        # There is no token or the user is missing newsletters,
        # try to subscribe the user to basket.
        subscribe_to = newsletters_to_subscribe or newsletters
        try:
            retval = basket.subscribe(
                email,
                subscribe_to,
                sync='Y',
                trigger_welcome='N'
            )
        except (requests.exceptions.RequestException, basket.BasketException) as exception:
            try:
                update_basket_task.retry()
            except (MaxRetriesExceededError, basket.BasketException):
                _email_basket_managers('subscribe', instance.user.email, exception.message)
            return

        # Don't call .save() on a userprofile from here, it would invoke us again
        # via the post-save signal, which would be pointless.
        UserProfile.objects.filter(pk=instance.pk).update(basket_token=retval['token'])
Exemple #23
0
 def test_lookup_user_token(self, mock_request):
     """Calling lookup_user with a token should not require an API key."""
     lookup_user(token='TOKEN')
     mock_request.assert_called_with('get', 'lookup-user',
                                     params={'token': 'TOKEN'})
Exemple #24
0
def update_basket_task(instance_id):
    """Update Basket Task.

    This task subscribes a user to Basket, if not already subscribed
    and then updates his data on the Phonebook DataExtension. The task
    retries on failure at most BASKET_TASK_MAX_RETRIES times and if it
    finally doesn't complete successfully, it emails the
    settings.BASKET_MANAGERS with details.

    """
    # This task is triggered by a post-save signal on UserProfile, so
    # we can't save() on UserProfile again while in here - if we were
    # running with CELERY_EAGER, we'd enter an infinite recursion until
    # Python died.

    from models import UserProfile
    try:
        instance = UserProfile.objects.get(pk=instance_id)
    except UserProfile.DoesNotExist:
        instance = None

    if not BASKET_ENABLED or not instance or not instance.is_vouched:
        return

    email = instance.user.email
    token = instance.basket_token

    if not token:
        # no token yet, they're probably not subscribed, so subscribe them.
        # pass sync='Y' so we wait for it to complete and get back the token.
        try:
            retval = basket.subscribe(
                email,
                [settings.BASKET_NEWSLETTER],
                sync='Y',
                trigger_welcome='N'
            )
        except (requests.exceptions.RequestException,
                basket.BasketException) as exception:
            try:
                update_basket_task.retry()
            except (MaxRetriesExceededError, basket.BasketException):
                _email_basket_managers('subscribe', instance.user.email,
                                       exception.message)
            return
        # Remember the token
        instance.basket_token = retval['token']
        token = retval['token']
        # Don't call .save() on a userprofile from here, it would invoke us again
        # via the post-save signal, which would be pointless.
        UserProfile.objects.filter(pk=instance.pk).update(basket_token=token)
    else:
        # They were already subscribed. See what email address they
        # have in exact target. If it has changed, we'll need to
        # unsubscribe the old address and subscribe the new one,
        # and save the new token.
        # This'll also return their subscriptions, so we can transfer them
        # to the new address if we need to.
        try:
            result = basket.lookup_user(token=token)
        except basket.BasketException as exception:
            try:
                update_basket_task.retry()
            except (MaxRetriesExceededError, basket.BasketException):
                msg = exception.message
                _email_basket_managers('update_phonebook', token, msg)
            return
        old_email = result['email']
        if old_email != email:
            try:
                # We do the new subscribe first, then the unsubscribe, so we don't
                # risk losing their subscriptions if the subscribe fails.
                # Subscribe to all the same newsletters.
                # Pass sync='Y' so we get back the new token right away
                subscribe_result = basket.subscribe(
                    email,
                    [settings.BASKET_NEWSLETTER],
                    sync='Y',
                    trigger_welcome='N',
                )
                # unsub all from the old token
                basket.unsubscribe(token=token, email=old_email,
                                   newsletters=[settings.BASKET_NEWSLETTER], optout='Y')
            except (requests.exceptions.RequestException,
                    basket.BasketException) as exception:
                try:
                    update_basket_task.retry()
                except (MaxRetriesExceededError, basket.BasketException):
                    _email_basket_managers('subscribe', email, exception.message)
                return
            # FIXME: We should also remove their previous phonebook record from Exact Target, but
            # basket doesn't have a custom API to do that. (basket never deletes anything.)

            # That was all successful. Update the token.
            instance.basket_token = subscribe_result['token']
            token = subscribe_result['token']
            # Don't call .save() on a userprofile from here, it would invoke us again
            # via the post-save signal, which would be pointless.
            UserProfile.objects.filter(pk=instance.pk).update(basket_token=token)
Exemple #25
0
def update_basket_task(instance_id):
    """Update Basket Task.

    This task subscribes a user to Basket, if not already subscribed
    and then updates his data on the Phonebook DataExtension. The task
    retries on failure at most BASKET_TASK_MAX_RETRIES times and if it
    finally doesn't complete successfully, it emails the
    settings.BASKET_MANAGERS with details.

    """
    # This task is triggered by a post-save signal on UserProfile, so
    # we can't save() on UserProfile again while in here - if we were
    # running with CELERY_EAGER, we'd enter an infinite recursion until
    # Python died.

    from models import UserProfile
    instance = UserProfile.objects.get(pk=instance_id)

    if not BASKET_ENABLED or not instance.is_vouched:
        return

    email = instance.user.email
    token = instance.basket_token

    if not token:
        # no token yet, they're probably not subscribed, so subscribe them.
        # pass sync='Y' so we wait for it to complete and get back the token.
        try:
            retval = basket.subscribe(email, [settings.BASKET_NEWSLETTER],
                                      sync='Y',
                                      trigger_welcome='N')
        except (requests.exceptions.RequestException,
                basket.BasketException) as exception:
            try:
                update_basket_task.retry()
            except (MaxRetriesExceededError, basket.BasketException):
                _email_basket_managers('subscribe', instance.user.email,
                                       exception.message)
            return
        # Remember the token
        instance.basket_token = retval['token']
        token = retval['token']
        # Don't call .save() on a userprofile from here, it would invoke us again
        # via the post-save signal, which would be pointless.
        UserProfile.objects.filter(pk=instance.pk).update(basket_token=token)
    else:
        # They were already subscribed. See what email address they
        # have in exact target. If it has changed, we'll need to
        # unsubscribe the old address and subscribe the new one,
        # and save the new token.
        # This'll also return their subscriptions, so we can transfer them
        # to the new address if we need to.
        try:
            result = basket.lookup_user(token=token)
        except basket.BasketException as exception:
            try:
                update_basket_task.retry()
            except (MaxRetriesExceededError, basket.BasketException):
                msg = exception.message
                _email_basket_managers('update_phonebook', token, msg)
            return
        old_email = result['email']
        if old_email != email:
            try:
                # We do the new subscribe first, then the unsubscribe, so we don't
                # risk losing their subscriptions if the subscribe fails.
                # Subscribe to all the same newsletters.
                # Pass sync='Y' so we get back the new token right away
                subscribe_result = basket.subscribe(
                    email,
                    [settings.BASKET_NEWSLETTER],
                    sync='Y',
                    trigger_welcome='N',
                )
                # unsub all from the old token
                basket.unsubscribe(token=token,
                                   email=old_email,
                                   newsletters=[settings.BASKET_NEWSLETTER],
                                   optout='Y')
            except (requests.exceptions.RequestException,
                    basket.BasketException) as exception:
                try:
                    update_basket_task.retry()
                except (MaxRetriesExceededError, basket.BasketException):
                    _email_basket_managers('subscribe', email,
                                           exception.message)
                return
            # FIXME: We should also remove their previous phonebook record from Exact Target, but
            # basket doesn't have a custom API to do that. (basket never deletes anything.)

            # That was all successful. Update the token.
            instance.basket_token = subscribe_result['token']
            token = subscribe_result['token']
            # Don't call .save() on a userprofile from here, it would invoke us again
            # via the post-save signal, which would be pointless.
            UserProfile.objects.filter(pk=instance.pk).update(
                basket_token=token)

    GroupMembership = get_model('groups', 'GroupMembership')
    Group = get_model('groups', 'Group')
    data = {}
    # What groups is the user in?
    user_group_pks = (instance.groups.filter(
        groupmembership__status=GroupMembership.MEMBER).values_list('pk',
                                                                    flat=True))
    for group in Group.objects.filter(functional_area=True):
        name = group.name.upper().replace(' ', '_')
        data[name] = 'Y' if group.id in user_group_pks else 'N'

    # User location if known
    if instance.geo_country:
        data['country'] = instance.geo_country.code
    if instance.geo_city:
        data['city'] = instance.geo_city.name

    # We have a token, proceed with the update
    try:
        basket.request('post',
                       'custom_update_phonebook',
                       token=token,
                       data=data)
    except (requests.exceptions.RequestException,
            basket.BasketException) as exception:
        try:
            update_basket_task.retry()
        except (MaxRetriesExceededError, basket.BasketException):
            _email_basket_managers('update_phonebook', email,
                                   exception.message)
Exemple #26
0
def update_basket_task(instance_id, newsletters=[]):
    """Update Basket Task.

    This task subscribes a user to Basket, if not already subscribed
    and then updates his data on the Phonebook DataExtension. The task
    retries on failure at most BASKET_TASK_MAX_RETRIES times and if it
    finally doesn't complete successfully, it emails the
    settings.BASKET_MANAGERS with details.

    """
    # This task is triggered by a post-save signal on UserProfile, so
    # we can't save() on UserProfile again while in here - if we were
    # running with CELERY_EAGER, we'd enter an infinite recursion until
    # Python died.

    from models import UserProfile
    try:
        instance = UserProfile.objects.get(pk=instance_id)
    except UserProfile.DoesNotExist:
        instance = None

    if (not BASKET_ENABLED or not instance or not newsletters
            or not waffle.switch_is_active('BASKET_SWITCH_ENABLED')):
        return

    email = instance.user.email
    token = instance.basket_token
    newsletters_to_subscribe = []

    if token:
        # They were already subscribed. See what email address they
        # have in exact target. If it has changed, we'll need to
        # unsubscribe the old address and subscribe the new one,
        # and save the new token.
        # This'll also return their subscriptions, so we can transfer them
        # to the new address if we need to.
        try:
            result = basket.lookup_user(token=token)
        except basket.BasketException as exception:
            try:
                update_basket_task.retry()
            except (MaxRetriesExceededError, basket.BasketException):
                msg = exception.message
                _email_basket_managers('update_phonebook', token, msg)
            return

        # Check if the users needs to be subscribed to additional newsletters.
        newsletters_to_subscribe = [
            nl for nl in newsletters if nl not in result['newsletters']
        ]

        old_email = result['email']
        if old_email != email:
            try:
                # We do the new subscribe first, then the unsubscribe, so we don't
                # risk losing their subscriptions if the subscribe fails.
                # Subscribe to all the same newsletters.
                # Pass sync='Y' so we get back the new token right away
                subscribe_result = basket.subscribe(
                    email,
                    result['newsletters'],
                    sync='Y',
                    trigger_welcome='N',
                )
                # unsubscribe all from the old token
                basket.unsubscribe(token=token, email=old_email, optout=True)
            except (requests.exceptions.RequestException,
                    basket.BasketException) as exception:
                try:
                    update_basket_task.retry()
                except (MaxRetriesExceededError, basket.BasketException):
                    _email_basket_managers('subscribe', email,
                                           exception.message)
                return

            # That was all successful. Update the token.
            token = subscribe_result['token']
            # Don't call .save() on a userprofile from here, it would invoke us again
            # via the post-save signal, which would be pointless.
            UserProfile.objects.filter(pk=instance.pk).update(
                basket_token=token)

    if not token or newsletters_to_subscribe:
        # There is no token or the user is missing newsletters,
        # try to subscribe the user to basket.
        subscribe_to = newsletters_to_subscribe or newsletters
        try:
            retval = basket.subscribe(email,
                                      subscribe_to,
                                      sync='Y',
                                      trigger_welcome='N')
        except (requests.exceptions.RequestException,
                basket.BasketException) as exception:
            try:
                update_basket_task.retry()
            except (MaxRetriesExceededError, basket.BasketException):
                _email_basket_managers('subscribe', instance.user.email,
                                       exception.message)
            return

        # Don't call .save() on a userprofile from here, it would invoke us again
        # via the post-save signal, which would be pointless.
        UserProfile.objects.filter(pk=instance.pk).update(
            basket_token=retval['token'])
Exemple #27
0
    def test_lookup_user_no_args(self, mock_request):
        """Calling lookup_user with no email or token raises an exception."""
        with self.assertRaises(BasketException):
            lookup_user()

        self.assertFalse(mock_request.called)
Exemple #28
0
 def test_lookup_user_token(self, mock_request):
     """Calling lookup_user with a token should not require an API key."""
     lookup_user(token='TOKEN')
     mock_request.assert_called_with('get',
                                     'lookup-user',
                                     params={'token': 'TOKEN'})