Example #1
0
 def test_parse_newsletters_not_groups_unsubscribe(self):
     """If newsletter slug is a group for SET mode, don't expand to group's newsletters."""
     record = {}
     to_sub, to_unsub = utils.parse_newsletters(record, utils.UNSUBSCRIBE, ['bowling'],
                                                set(['bowling', 'surfing', 'extorting']))
     self.assertEqual(to_unsub, ['bowling'])
     self.assertEqual(record['BOWLING_FLG'], 'N')
Example #2
0
 def test_parse_newsletters_for_groups(self):
     """If newsletter slug is a group for SUBSCRIBE, expand to group's newsletters."""
     record = {}
     to_sub, to_unsub = utils.parse_newsletters(record, utils.SUBSCRIBE, ['bowling'], set())
     self.assertEqual(set(to_sub), set(['surfing', 'extorting']))
     self.assertEqual(record['SURFING_FLG'], 'Y')
     self.assertEqual(record['EXTORTING_FLG'], 'Y')
Example #3
0
def upsert_contact(api_call_type, data, user_data):
    """
    Update or insert (upsert) a contact record in SFDC

    @param int api_call_type: What kind of API call it was. Could be
        SUBSCRIBE, UNSUBSCRIBE, or SET.
    @param dict data: POST data from the form submission
    @param dict user_data: existing contact data from SFDC
    @return: token, created
    """
    update_data = data.copy()
    forced_optin = data.pop('optin', False)
    if 'format' in data:
        update_data['format'] = 'T' if data['format'].upper().startswith('T') else 'H'

    newsletters = parse_newsletters_csv(data.get('newsletters'))

    if user_data:
        cur_newsletters = user_data.get('newsletters', None)
    else:
        cur_newsletters = None

    # check for and remove transactional newsletters
    if api_call_type == SUBSCRIBE:
        all_transactionals = set(get_transactional_message_ids())
        newsletters_set = set(newsletters)
        transactionals = newsletters_set & all_transactionals
        if transactionals:
            newsletters = list(newsletters_set - transactionals)
            send_transactional_messages(update_data, user_data, list(transactionals))
            if not newsletters:
                # no regular newsletters
                return None, None

    # Set the newsletter flags in the record by comparing to their
    # current subscriptions.
    update_data['newsletters'] = parse_newsletters(api_call_type, newsletters, cur_newsletters)

    if api_call_type != UNSUBSCRIBE:
        # Are they subscribing to any newsletters that don't require confirmation?
        # When including any newsletter that does not
        # require confirmation, user gets a pass on confirming and goes straight
        # to confirmed.
        to_subscribe = [nl for nl, sub in update_data['newsletters'].iteritems() if sub]
        if to_subscribe and not (forced_optin or
                                 (user_data and user_data.get('optin'))):
            exempt_from_confirmation = Newsletter.objects \
                .filter(slug__in=to_subscribe, requires_double_optin=False) \
                .exists()
            if exempt_from_confirmation:
                update_data['optin'] = True

        # record source URL
        nl_map = newsletter_map()
        source_url = update_data.get('source_url')
        email = update_data.get('email')
        if not email:
            email = user_data.get('email') if user_data else None

        if email:
            # send all newsletters whether already subscribed or not
            # bug 1308971
            # if api_call_type == SET this is pref center, so only send new subscriptions
            nl_list = newsletters if api_call_type == SUBSCRIBE else to_subscribe
            for nlid in nl_list:
                record_source_url.delay(email, source_url, nl_map[nlid])

    if user_data is None:
        # no user found. create new one.
        update_data['token'] = generate_token()
        if settings.MAINTENANCE_MODE:
            sfdc_add_update.delay(update_data)
        else:
            # don't catch exceptions here. SalesforceError subclasses will retry.
            sfdc.add(update_data)

        return update_data['token'], True

    if forced_optin and not user_data.get('optin'):
        update_data['optin'] = True

    # they opted out of email before, but are subscribing again
    # clear the optout flag
    if api_call_type != UNSUBSCRIBE and user_data.get('optout'):
        update_data['optout'] = False

    # update record
    if user_data and user_data.get('token'):
        token = user_data['token']
    else:
        token = update_data['token'] = generate_token()

    if settings.MAINTENANCE_MODE:
        sfdc_add_update.delay(update_data, user_data)
    else:
        sfdc.update(user_data, update_data)

    return token, False
Example #4
0
def update_user(data, email, token, api_call_type, optin):
    """Task for updating user's preferences and newsletters.

    :param dict data: POST data from the form submission
    :param string email: User's email address
    :param string token: User's token. If None, the token will be
        looked up, and if no token is found, one will be created for the
        given email.
    :param int api_call_type: What kind of API call it was. Could be
        SUBSCRIBE, UNSUBSCRIBE, or SET.
    :param boolean optin: Whether the user should go through the
        double-optin process or not. If ``optin`` is ``True`` then
        the user should bypass the double-optin process.

    :returns: One of the return codes UU_ALREADY_CONFIRMED,
        etc. (see code) to indicate what case we figured out we were
        doing.  (These are primarily for tests to use.)
    :raises: NewsletterException if there are any errors that would be
        worth retrying. Our task wrapper will retry in that case.
    """
    # If token is missing, find it or generate it.
    if not token:
        sub, user_data, created = lookup_subscriber(email=email)
        token = sub.token

    # Parse the parameters
    # `record` will contain the data we send to ET in the format they want.
    record = {
        'EMAIL_ADDRESS_': email,
        'TOKEN': token,
        'EMAIL_PERMISSION_STATUS_': 'I',
        'MODIFIED_DATE_': gmttime(),
    }

    extra_fields = {
        'country': 'COUNTRY_',
        'lang': 'LANGUAGE_ISO2',
        'source_url': 'SOURCE_URL',
    }

    # Optionally add more fields
    for field in extra_fields:
        if field in data:
            record[extra_fields[field]] = data[field]

    lang = record.get('LANGUAGE_ISO2', '') or ''

    # Get the user's current settings from ET, if any
    user_data = get_user_data(token=token)
    # If we don't find the user, get_user_data returns None. Create
    # a minimal dictionary to use going forward. This will happen
    # often due to new people signing up.
    if user_data is None:
        user_data = {
            'email': email,
            'token': token,
            'master': False,
            'pending': False,
            'confirmed': False,
            'lang': lang,
            'status': 'ok',
        }

    if lang:
        # User asked for a language change. Use the new language from
        # here on.
        user_data['lang'] = lang
    else:
        # Use `lang` as a shorter reference to user_data['lang']
        lang = user_data['lang']

    # We need an HTML/Text format choice for sending welcome messages, and
    # optionally to update their ET record
    if 'format' in data:  # Submitted in call
        fmt = 'T' if data.get('format', 'H').upper().startswith('T') else 'H'
        # We only set the format in ET if the call asked us to
        record['EMAIL_FORMAT_'] = fmt
    elif 'format' in user_data:  # Existing user preference
        fmt = user_data['format']
    else:  # Default to 'H'
        fmt = 'H'
    # From here on, fmt is either 'H' or 'T', preferring 'H'

    newsletters = [x.strip() for x in data.get('newsletters', '').split(',')]

    cur_newsletters = user_data.get('newsletters', None)
    if cur_newsletters is not None:
        cur_newsletters = set(cur_newsletters)

    # Set the newsletter flags in the record by comparing to their
    # current subscriptions.
    to_subscribe, to_unsubscribe = parse_newsletters(record, api_call_type,
                                                     newsletters,
                                                     cur_newsletters)

    # Are they subscribing to any newsletters that don't require confirmation?
    # When including any newsletter that does not
    # require confirmation, user gets a pass on confirming and goes straight
    # to confirmed.
    exempt_from_confirmation = optin or Newsletter.objects\
        .filter(slug__in=to_subscribe, requires_double_optin=False)\
        .exists()

    # Send welcomes when api_call_type is SUBSCRIBE and trigger_welcome
    # arg is absent or 'Y'.
    should_send_welcomes = data.get('trigger_welcome', 'Y') == 'Y' and api_call_type == SUBSCRIBE

    MASTER = settings.EXACTTARGET_DATA
    OPT_IN = settings.EXACTTARGET_OPTIN_STAGE

    if user_data['confirmed']:
        # The user is already confirmed.
        # Just add any new subs to whichever of master or optin list is
        # appropriate, and send welcomes.
        target_et = MASTER if user_data['master'] else OPT_IN
        apply_updates(target_et, record)
        if should_send_welcomes:
            send_welcomes(user_data, to_subscribe, fmt)
        return_code = UU_ALREADY_CONFIRMED
    elif exempt_from_confirmation:
        # This user is not confirmed, but they
        # qualify to be excepted from confirmation.
        if user_data['pending']:
            # We were waiting for them to confirm.  Update the data in
            # their record (currently in the Opt-in table), then go
            # ahead and confirm them. This will also send welcomes.
            apply_updates(OPT_IN, record)
            confirm_user(user_data['token'], user_data)
            return_code = UU_EXEMPT_PENDING
        else:
            # Brand new user: Add them directly to master subscriber DB
            # and send welcomes.
            record['CREATED_DATE_'] = gmttime()
            apply_updates(MASTER, record)
            if should_send_welcomes:
                send_welcomes(user_data, to_subscribe, fmt)
            return_code = UU_EXEMPT_NEW
    else:
        # This user must confirm
        if user_data['pending']:
            return_code = UU_MUST_CONFIRM_PENDING
        else:
            # Creating a new record, need a couple more fields
            record['CREATED_DATE_'] = gmttime()
            record['SubscriberKey'] = record['TOKEN']
            record['EmailAddress'] = record['EMAIL_ADDRESS_']
            return_code = UU_MUST_CONFIRM_NEW
        # Create or update OPT_IN record and send email telling them (or
        # reminding them) to confirm.
        apply_updates(OPT_IN, record)
        send_confirm_notice(email, token, lang, fmt, to_subscribe)
    return return_code
Example #5
0
def update_get_involved(interest_id, lang, name, email, country, email_format,
                        subscribe, message, source_url):
    """Record a users interest and details for contribution."""
    try:
        interest = Interest.objects.get(interest_id=interest_id)
    except Interest.DoesNotExist:
        # invalid request; no need to raise exception and retry
        return

    email_format = 'T' if email_format.upper().startswith('T') else 'H'

    # Get the user's current settings from ET, if any
    user = get_user_data(email=email)

    record = {
        'EMAIL_ADDRESS_': email,
        'MODIFIED_DATE_': gmttime(),
        'LANGUAGE_ISO2': lang,
        'COUNTRY_': country,
        'GET_INVOLVED_FLG': 'Y',
    }
    if user:
        token = user['token']
        Subscriber.objects.get_and_sync(email, token)
        if 'get-involved' not in user.get('newsletters', []):
            record['GET_INVOLVED_DATE'] = gmttime()
    else:
        sub, created = Subscriber.objects.get_or_create(email=email)
        token = sub.token
        record['EMAIL_FORMAT_'] = email_format
        record['GET_INVOLVED_DATE'] = gmttime()
        # only want source url for first contact
        if source_url:
            record['SOURCE_URL'] = source_url

    record['TOKEN'] = token
    if subscribe:
        # TODO: 'get-involved' not added to ET yet, so can't use it yet.
        # will go in this list when ready.
        newsletters = ['about-mozilla']
        if user:
            cur_newsletters = user.get('newsletters', None)
            if cur_newsletters is not None:
                cur_newsletters = set(cur_newsletters)
        else:
            cur_newsletters = None

        # Set the newsletter flags in the record by comparing to their
        # current subscriptions.
        to_subscribe, _ = parse_newsletters(record, SUBSCRIBE, newsletters, cur_newsletters)
    else:
        to_subscribe = None

    apply_updates(settings.EXACTTARGET_DATA, record)
    apply_updates(settings.EXACTTARGET_INTERESTS, {
        'TOKEN': token,
        'INTEREST': interest_id,
    })
    welcome_id = mogrify_message_id(interest.welcome_id, lang, email_format)
    send_message.delay(welcome_id, email, token, email_format)
    interest.notify_stewards(name, email, lang, message)

    if to_subscribe:
        if not user:
            user = {
                'email': email,
                'token': token,
                'lang': lang,
            }
        send_welcomes(user, to_subscribe, email_format)
Example #6
0
 def test_parse_newsletters_not_groups_set(self):
     """If newsletter slug is a group for SET mode, don't expand to group's newsletters."""
     record = {}
     to_sub, to_unsub = utils.parse_newsletters(record, utils.SET, ['bowling'], set())
     self.assertEqual(to_sub, ['bowling'])
     self.assertEqual(record['BOWLING_FLG'], 'Y')
Example #7
0
 def test_parse_newsletters_not_groups_set(self):
     """If newsletter slug is a group for SET mode, don't expand to group's newsletters."""
     subs = utils.parse_newsletters(utils.SET, ['bowling'], list())
     self.assertDictEqual(subs, {'bowling': True})
Example #8
0
 def test_parse_newsletters_for_groups(self):
     """If newsletter slug is a group for SUBSCRIBE, expand to group's newsletters."""
     subs = utils.parse_newsletters(utils.SUBSCRIBE, ['bowling'], list())
     self.assertTrue(subs['surfing'])
     self.assertTrue(subs['extorting'])
Example #9
0
 def test_parse_newsletters_not_groups_unsubscribe(self):
     """If newsletter slug is a group for SET mode, don't expand to group's newsletters."""
     subs = utils.parse_newsletters(utils.UNSUBSCRIBE, ['bowling'],
                                    ['bowling', 'surfing', 'extorting'])
     self.assertDictEqual(subs, {'bowling': False})