def test_user_set_bad_language(self): """If the user view is sent a POST request with an invalid language, it fails. """ token = generate_token() resp = self.client.post('/news/user/{}/'.format(token), {'fake': 'data', 'lang': '55'}) self.assertEqual(resp.status_code, 400) data = json.loads(resp.content) self.assertEqual(data['status'], 'error') self.assertEqual(data['desc'], 'invalid language')
def test_user_not_in_et(self, sfmc_mock): """A user not found in ET should produce an error response.""" sfmc_mock.get_row.side_effect = NewsletterException('DANGER!') token = generate_token() resp = self.client.get('/news/user/{}/'.format(token)) self.assertEqual(resp.status_code, 400) resp_data = json.loads(resp.content) self.assertDictEqual(resp_data, { 'status': 'error', 'desc': 'DANGER!', 'code': errors.BASKET_NETWORK_FAILURE, })
def setUp(self): self.token = generate_token() self.email = '*****@*****.**' # User data in format that get_user_data() returns it self.get_user_data = { 'email': self.email, 'token': self.token, 'format': 'H', 'country': 'us', 'lang': 'en', 'newsletters': ['slug'], 'status': 'ok', }
def test_user_not_in_sf(self, sfdc_mock): """A user not found in SFDC should produce an error response.""" sfdc_mock.get.side_effect = NewsletterException('DANGER!') token = generate_token() resp = self.client.get('/news/user/{}/'.format(token)) self.assertEqual(resp.status_code, 400) resp_data = json.loads(resp.content) self.assertDictEqual( resp_data, { 'status': 'error', 'desc': 'DANGER!', 'code': errors.BASKET_UNKNOWN_ERROR, })
def setUp(self): self.token = generate_token() self.url = '/news/custom_update_student_ambassadors/%s/' % \ self.token self.data = {'FIRST_NAME': 'Testy', 'LAST_NAME': 'McTestface', 'EMAIL_ADDRESS': '*****@*****.**', 'STUDENTS_CURRENT_STATUS': 'student', 'STUDENTS_SCHOOL': 'Testington U', 'STUDENTS_GRAD_YEAR': '2014', 'STUDENTS_MAJOR': 'computer engineering', 'COUNTRY': 'GR', 'STUDENTS_CITY': 'Testington', 'STUDENTS_ALLOW_SHARE': 'N'}
def update_fxa_info(email, lang, fxa_id, source_url=None, skip_welcome=False): user = get_external_user_data(email=email) record = { 'EMAIL_ADDRESS_': email, 'FXA_ID': fxa_id, 'MODIFIED_DATE_': gmttime(), 'FXA_LANGUAGE_ISO2': lang, } if user: welcome_format = user['format'] token = user['token'] else: welcome_format = 'H' token = generate_token() # only want source url for first contact record['SOURCE_URL'] = source_url or 'https://accounts.firefox.com' record['TOKEN'] = token apply_updates(settings.EXACTTARGET_DATA, record) if not skip_welcome: welcome = mogrify_message_id(FXACCOUNT_WELCOME, lang, welcome_format) send_message.delay(welcome, email, token, welcome_format)
def process_donation(data): timestamp = data['timestamp'] data = data['data'] get_lock(data['email']) # tells the backend to leave the "subscriber" flag alone contact_data = {'_set_subscriber': False} if 'first_name' in data: if data.get('first_name'): contact_data['first_name'] = data['first_name'] if data.get('last_name'): contact_data['last_name'] = data['last_name'] elif data.get('last_name'): names = data['last_name'].rsplit(None, 1) if len(names) == 2: first, last = names else: first, last = '', names[0] if first: contact_data['first_name'] = first if last: contact_data['last_name'] = last user_data = get_user_data(email=data['email'], extra_fields=['id']) if user_data: if contact_data and ( ('first_name' in contact_data and contact_data['first_name'] != user_data['first_name']) or ('last_name' in contact_data and contact_data['last_name'] != user_data['last_name'])): sfdc.update(user_data, contact_data) else: contact_data['token'] = generate_token() contact_data['email'] = data['email'] contact_data['record_type'] = settings.DONATE_CONTACT_RECORD_TYPE sfdc.add(contact_data) # fetch again to get ID user_data = get_user_data(email=data.get('email'), extra_fields=['id']) if not user_data: # retry here to make sure we associate the donation data with the proper account raise RetryTask('User not yet available') # add opportunity donation = { 'RecordTypeId': settings.DONATE_OPP_RECORD_TYPE, 'Name': 'Foundation Donation', 'Donation_Contact__c': user_data['id'], 'StageName': 'Closed Won', 'CloseDate': timestamp, 'Amount': float(data['donation_amount']), 'Currency__c': data['currency'].upper(), 'Payment_Source__c': data['service'], 'PMT_Transaction_ID__c': data['transaction_id'], 'Payment_Type__c': 'Recurring' if data['recurring'] else 'One-Time', } for dest_name, source_name in DONATION_OPTIONAL_FIELDS.items(): value = data.get(source_name) if value: donation[dest_name] = value sfdc.opportunity.create(donation)
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 check_confirmation(self, user_in_master, user_in_optin, user_in_confirmed, newsletter_with_required_confirmation, newsletter_without_required_confirmation, is_english, type, expected_result, is_optin=False): # Generic test routine - given a bunch of initial conditions and # an expected result, set up the conditions, make the call, # and verify we get the expected result. email = "*****@*****.**" token = generate_token() # What should get_user_data return? user = {} if user_in_master or user_in_confirmed or user_in_optin: user['status'] = 'ok' user['email'] = email user['format'] = 'T' user['token'] = token user['master'] = user_in_master user['confirmed'] = user_in_master or user_in_confirmed user['pending'] = user_in_optin and not user_in_confirmed # start with none so whatever we call # update_user with is a new subscription user['newsletters'] = [] else: # User not in Exact Target at all user = None # Call data data = {} if is_english: data['lang'] = 'en' data['country'] = 'us' else: data['lang'] = 'fr' data['country'] = 'fr' # make some newsletters nl_required = models.Newsletter.objects.create( slug='slug1', vendor_id='VENDOR1', requires_double_optin=True, ) nl_not_required = models.Newsletter.objects.create( slug='slug2', vendor_id='VENDOR2', requires_double_optin=False, ) newsletters = [] if newsletter_with_required_confirmation: newsletters.append(nl_required.slug) if newsletter_without_required_confirmation: newsletters.append(nl_not_required.slug) data['newsletters'] = ','.join(newsletters) # Mock data from ET with patch('news.tasks.get_user_data') as get_user_data: get_user_data.return_value = user # Don't actually call ET with patch('news.tasks.apply_updates'): with patch('news.tasks.send_welcomes'): with patch('news.tasks.send_confirm_notice'): rc = update_user(data, email, token, type, is_optin) self.assertEqual(expected_result, rc)
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. """ # Get the user's current settings from ET, if any user_data = get_user_data(email=email, 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 or generate_token(), 'master': False, 'pending': False, 'confirmed': False, 'lang': '', 'status': 'ok', } token = user_data['token'] email = user_data['email'] # 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', 'first_name': 'FIRST_NAME', 'last_name': 'LAST_NAME', } # Optionally add more fields for field, et_id in extra_fields.items(): if field in data: record[et_id] = data[field] lang = record.get('LANGUAGE_ISO2', '') or '' 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'] if 'get-involved' not in user.get('newsletters', []): record['GET_INVOLVED_DATE'] = gmttime() else: token = generate_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 setUp(self): self.token = generate_token() self.url = '/news/custom_update_phonebook/%s/' % self.token