コード例 #1
0
def unsubscribe(request, token):
    data = request.POST.dict()
    data['token'] = token

    if data.get('optout', 'N') == 'Y':
        data['newsletters'] = ','.join(newsletter_slugs())

    return update_user_task(request, UNSUBSCRIBE, data)
コード例 #2
0
def unsubscribe(request, token):
    data = request.POST.dict()
    data['token'] = token

    if data.get('optout', 'N') == 'Y':
        data['optout'] = True
        data['newsletters'] = ','.join(newsletter_slugs())

    return update_user_task(request, UNSUBSCRIBE, data)
コード例 #3
0
ファイル: utils.py プロジェクト: pmclanahan/basket
def look_for_user(database, email, token, fields):
    """Try to get the user's data from the specified ET database.
    If found and the database is not the 'Confirmed' database,
    return it (a dictionary, see get_user_data).
    If found and it's the 'Confirmed' database, just return True.
    If not found, return None.
    Any other exception just propagates and needs to be handled
    by the caller.
    """
    ext = ExactTargetDataExt(settings.EXACTTARGET_USER,
                             settings.EXACTTARGET_PASS)
    try:
        user = ext.get_record(database,
                              email or token,
                              fields,
                              'EMAIL_ADDRESS_' if email else 'TOKEN')
    except NewsletterNoResultsException:
        return None
    if database == settings.EXACTTARGET_CONFIRMATION:
        return True
    newsletters = []
    for slug in newsletter_slugs():
        vendor_id = slug_to_vendor_id(slug)
        flag = "%s_FLG" % vendor_id
        if user.get(flag, 'N') == 'Y':
            newsletters.append(slug)
    user_data = {
        'status': 'ok',
        'email': user['EMAIL_ADDRESS_'],
        'format': user['EMAIL_FORMAT_'] or 'H',
        'country': user['COUNTRY_'] or '',
        'lang': user['LANGUAGE_ISO2'] or '',  # Never None
        'token': user['TOKEN'],
        'created-date': user['CREATED_DATE_'],
        'newsletters': newsletters,
    }
    return user_data
コード例 #4
0
ファイル: utils.py プロジェクト: pmclanahan/basket
def parse_newsletters(record, api_call_type, newsletters, cur_newsletters):
    """Utility function to take a list of newsletters and according
    the type of action (subscribe, unsubscribe, and set) set the
    appropriate flags in `record` which is a dict of parameters that
    will be sent to the email provider.

    Parameters are only set for the newsletters whose subscription
    status needs to change, so that we don't unnecessarily update the
    last modification timestamp of newsletters.

    :param dict record: Parameters that will be sent to ET
    :param integer api_call_type: SUBSCRIBE means add these newsletters to the
        user's subscriptions if not already there, UNSUBSCRIBE means remove
        these newsletters from the user's subscriptions if there, and SET
        means change the user's subscriptions to exactly this set of
        newsletters.
    :param list newsletters: List of the slugs of the newsletters to be
        subscribed, unsubscribed, or set.
    :param set cur_newsletters: Set of the slugs of the newsletters that
        the user is currently subscribed to. None if there was an error.
    :returns: (to_subscribe, to_unsubscribe) - lists of slugs of the
        newsletters that we will request new subscriptions to, or request
        unsubscription from, respectively.
    """

    to_subscribe = []
    to_unsubscribe = []

    if api_call_type == SUBSCRIBE:
        grouped_newsletters = set()
        for nl in newsletters:
            group_nl = newsletter_group_newsletter_slugs(nl)
            if group_nl:
                grouped_newsletters.update(group_nl)
            else:
                grouped_newsletters.add(nl)

        newsletters = list(grouped_newsletters)

    if api_call_type == SUBSCRIBE or api_call_type == SET:
        # Subscribe the user to these newsletters if not already
        for nl in newsletters:
            name = newsletter_field(nl)
            if name and (cur_newsletters is None or
                                 nl not in cur_newsletters):
                record['%s_FLG' % name] = 'Y'
                record['%s_DATE' % name] = date.today().strftime('%Y-%m-%d')
                to_subscribe.append(nl)

    if api_call_type == UNSUBSCRIBE or api_call_type == SET:
        # Unsubscribe the user to these newsletters

        if api_call_type == SET:
            # Unsubscribe from the newsletters currently subscribed to
            # but not in the new list
            if cur_newsletters is not None:
                unsubs = cur_newsletters - set(newsletters)
            else:
                subs = set(newsletters)
                all = set(newsletter_slugs())
                unsubs = all - subs
        else:  # type == UNSUBSCRIBE
            # unsubscribe from the specified newsletters
            unsubs = newsletters

        for nl in unsubs:
            # Unsubscribe from any unsubs that the user is currently subbed to
            name = newsletter_field(nl)
            if name and (cur_newsletters is None or nl in cur_newsletters):
                record['%s_FLG' % name] = 'N'
                record['%s_DATE' % name] = date.today().strftime('%Y-%m-%d')
                to_unsubscribe.append(nl)
    return to_subscribe, to_unsubscribe
コード例 #5
0
ファイル: utils.py プロジェクト: pmclanahan/basket
def update_user_task(request, api_call_type, data=None, optin=True, sync=False):
    """Call the update_user task async with the right parameters.

    If sync==True, be sure to do the update synchronously and include the token
    in the response.  Otherwise, basket has the option to do it all later
    in the background.
    """
    from news.tasks import update_user

    sub = getattr(request, 'subscriber', None)
    data = data or request.POST.dict()

    newsletters = data.get('newsletters', None)
    if newsletters:
        if api_call_type == SUBSCRIBE:
            all_newsletters = newsletter_and_group_slugs()
        else:
            all_newsletters = newsletter_slugs()
        for nl in [x.strip() for x in newsletters.split(',')]:
            if nl not in all_newsletters:
                return HttpResponseJSON({
                    'status': 'error',
                    'desc': 'invalid newsletter',
                    'code': errors.BASKET_INVALID_NEWSLETTER,
                }, 400)

    if 'lang' in data:
        if not language_code_is_valid(data['lang']):
            return HttpResponseJSON({
                'status': 'error',
                'desc': 'invalid language',
                'code': errors.BASKET_INVALID_LANGUAGE,
            }, 400)
    elif 'accept_lang' in data:
        lang = get_best_language(get_accept_languages(data['accept_lang']))
        if lang:
            data['lang'] = lang
            del data['accept_lang']
        else:
            return HttpResponseJSON({
                'status': 'error',
                'desc': 'invalid language',
                'code': errors.BASKET_INVALID_LANGUAGE,
            }, 400)

    email = data.get('email', sub.email if sub else None)
    if not email:
        return HttpResponseJSON({
            'status': 'error',
            'desc': MSG_EMAIL_OR_TOKEN_REQUIRED,
            'code': errors.BASKET_USAGE_ERROR,
        }, 400)

    if sync:
        if sub:
            created = False
        else:
            # We need a token for this user. If we don't have a Subscriber
            # object for them already, we'll need to find or make one,
            # checking ET first if need be.
            sub, user_data, created = lookup_subscriber(email=email)

        update_user.delay(data, sub.email, sub.token, api_call_type, optin)
        return HttpResponseJSON({
            'status': 'ok',
            'token': sub.token,
            'created': created,
        })
    else:
        update_user.delay(data, email, None, api_call_type, optin)
        return HttpResponseJSON({
            'status': 'ok',
        })
コード例 #6
0
def update_user_task(request, api_call_type, data=None, optin=True, sync=False):
    """Call the update_user task async with the right parameters.

    If sync==True, be sure to include the token in the response.
    Otherwise, basket can just do everything in the background.
    """
    data = data or request.POST.dict()

    newsletters = data.get('newsletters', None)
    if newsletters:
        newsletters = [x.strip() for x in newsletters.split(',')]
        if api_call_type == SUBSCRIBE:
            all_newsletters = newsletter_and_group_slugs()
        else:
            all_newsletters = newsletter_slugs()

        private_newsletters = newsletter_private_slugs()

        for nl in newsletters:
            if nl not in all_newsletters:
                return HttpResponseJSON({
                    'status': 'error',
                    'desc': 'invalid newsletter',
                    'code': errors.BASKET_INVALID_NEWSLETTER,
                }, 400)

            if api_call_type != UNSUBSCRIBE and nl in private_newsletters:
                if not request.is_secure():
                    return HttpResponseJSON({
                        'status': 'error',
                        'desc': 'private newsletter subscription requires SSL',
                        'code': errors.BASKET_SSL_REQUIRED,
                    }, 401)

                if not has_valid_api_key(request):
                    return HttpResponseJSON({
                        'status': 'error',
                        'desc': 'private newsletter subscription requires a valid API key',
                        'code': errors.BASKET_AUTH_ERROR,
                    }, 401)

    if 'lang' in data:
        if not language_code_is_valid(data['lang']):
            return HttpResponseJSON({
                'status': 'error',
                'desc': 'invalid language',
                'code': errors.BASKET_INVALID_LANGUAGE,
            }, 400)
    elif 'accept_lang' in data:
        lang = get_best_language(get_accept_languages(data['accept_lang']))
        if lang:
            data['lang'] = lang
            del data['accept_lang']
        else:
            return HttpResponseJSON({
                'status': 'error',
                'desc': 'invalid language',
                'code': errors.BASKET_INVALID_LANGUAGE,
            }, 400)

    email = data.get('email')
    token = data.get('token')
    if not (email or token):
        return HttpResponseJSON({
            'status': 'error',
            'desc': MSG_EMAIL_OR_TOKEN_REQUIRED,
            'code': errors.BASKET_USAGE_ERROR,
        }, 400)

    if sync:
        try:
            user_data, created = get_or_create_user_data(email=email, token=token)
        except NewsletterException as e:
            return newsletter_exception_response(e)

        update_user.delay(data, user_data['email'], user_data['token'], api_call_type, optin,
                          start_time=time())
        return HttpResponseJSON({
            'status': 'ok',
            'token': user_data['token'],
            'created': created,
        })
    else:
        update_user.delay(data, email, token, api_call_type, optin,
                          start_time=time())
        return HttpResponseJSON({
            'status': 'ok',
        })
コード例 #7
0
ファイル: test_newsletters.py プロジェクト: pmclanahan/basket
 def test_newsletter_slugs(self):
     self.assertEqual(set(newsletters.newsletter_slugs()),
                      set(['bowling', 'surfing', 'extorting']))
コード例 #8
0
 def test_newsletter_slugs(self):
     self.assertEqual(set(newsletters.newsletter_slugs()),
                      {'bowling', 'surfing', 'extorting', 'papers'})
コード例 #9
0
def update_user_task(request, api_call_type, data=None, optin=False, sync=False):
    """Call the update_user task async with the right parameters.

    If sync==True, be sure to include the token in the response.
    Otherwise, basket can just do everything in the background.
    """
    data = data or request.POST.dict()

    newsletters = parse_newsletters_csv(data.get('newsletters'))
    if newsletters:
        if api_call_type == SUBSCRIBE:
            all_newsletters = newsletter_and_group_slugs() + get_transactional_message_ids()
        else:
            all_newsletters = newsletter_slugs()

        private_newsletters = newsletter_private_slugs()

        for nl in newsletters:
            if nl not in all_newsletters:
                return HttpResponseJSON({
                    'status': 'error',
                    'desc': 'invalid newsletter',
                    'code': errors.BASKET_INVALID_NEWSLETTER,
                }, 400)

            if api_call_type != UNSUBSCRIBE and nl in private_newsletters:
                if not request.is_secure():
                    return HttpResponseJSON({
                        'status': 'error',
                        'desc': 'private newsletter subscription requires SSL',
                        'code': errors.BASKET_SSL_REQUIRED,
                    }, 401)

                if not has_valid_api_key(request):
                    return HttpResponseJSON({
                        'status': 'error',
                        'desc': 'private newsletter subscription requires a valid API key',
                        'code': errors.BASKET_AUTH_ERROR,
                    }, 401)

    if 'lang' in data:
        if not language_code_is_valid(data['lang']):
            data['lang'] = 'en'
    elif 'accept_lang' in data:
        lang = get_best_language(get_accept_languages(data['accept_lang']))
        if lang:
            data['lang'] = lang
            del data['accept_lang']
        else:
            data['lang'] = 'en'

    email = data.get('email')
    token = data.get('token')
    if not (email or token):
        return HttpResponseJSON({
            'status': 'error',
            'desc': MSG_EMAIL_OR_TOKEN_REQUIRED,
            'code': errors.BASKET_USAGE_ERROR,
        }, 400)

    if optin:
        data['optin'] = True

    if api_call_type == SUBSCRIBE and email and data.get('newsletters'):
        # only rate limit here so we don't rate limit errors.
        if is_ratelimited(request, group='news.views.update_user_task.subscribe',
                          key=lambda x, y: '%s-%s' % (data['newsletters'], email),
                          rate=EMAIL_SUBSCRIBE_RATE_LIMIT, increment=True):
            raise Ratelimited()

    if api_call_type == SET and token and data.get('newsletters'):
        # only rate limit here so we don't rate limit errors.
        if is_ratelimited(request, group='news.views.update_user_task.set',
                          key=lambda x, y: '%s-%s' % (data['newsletters'], token),
                          rate=EMAIL_SUBSCRIBE_RATE_LIMIT, increment=True):
            raise Ratelimited()

    if sync:
        statsd.incr('news.views.subscribe.sync')
        if settings.MAINTENANCE_MODE and not settings.MAINTENANCE_READ_ONLY:
            # save what we can
            upsert_user.delay(api_call_type, data, start_time=time())
            # have to error since we can't return a token
            return HttpResponseJSON({
                'status': 'error',
                'desc': 'sync is not available in maintenance mode',
                'code': errors.BASKET_NETWORK_FAILURE,
            }, 400)

        try:
            user_data = get_user_data(email=email, token=token)
        except NewsletterException as e:
            return newsletter_exception_response(e)

        if not user_data:
            if not email:
                # must have email to create a user
                return HttpResponseJSON({
                    'status': 'error',
                    'desc': MSG_EMAIL_OR_TOKEN_REQUIRED,
                    'code': errors.BASKET_USAGE_ERROR,
                }, 400)

        token, created = upsert_contact(api_call_type, data, user_data)
        return HttpResponseJSON({
            'status': 'ok',
            'token': token,
            'created': created,
        })
    else:
        upsert_user.delay(api_call_type, data, start_time=time())
        return HttpResponseJSON({
            'status': 'ok',
        })