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')
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')
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
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
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)
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')
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})
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'])
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})