def process_petition_signature(data): """ Add petition signature to SFDC """ data = data['form'] get_lock(data['email']) # tells the backend to leave the "subscriber" flag alone contact_data = {'_set_subscriber': False} contact_data.update( {k: data[k] for k in PETITION_CONTACT_FIELDS if data.get(k)}) user_data = get_user_data(email=data['email'], extra_fields=['id']) if user_data: 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') if data.get('email_subscription', False): upsert_user.delay( SUBSCRIBE, { 'token': user_data['token'], 'lang': data.get('lang', 'en-US'), 'newsletters': 'mozilla-foundation', 'source_url': data['source_url'], }) campaign_member = { 'CampaignId': data['campaign_id'], 'ContactId': user_data['id'], 'Full_URL__c': data['source_url'], 'Status': 'Signed', } comments = data.get('comments') if comments: campaign_member['Petition_Comments__c'] = comments[:500] metadata = data.get('metadata') if metadata: campaign_member['Petition_Flex__c'] = json.dumps(metadata)[:500] try: sfdc.campaign_member.create(campaign_member) except sfapi.SalesforceMalformedRequest as e: if e.content and e.content[0].get('errorCode') == 'DUPLICATE_VALUE': # already in the system, ignore pass else: raise
def process_petition_signature(data): """ Add petition signature to SFDC """ data = data['form'] get_lock(data['email']) # tells the backend to leave the "subscriber" flag alone contact_data = {'_set_subscriber': False} contact_data.update({k: data[k] for k in PETITION_CONTACT_FIELDS if data.get(k)}) user_data = get_user_data(email=data['email'], extra_fields=['id']) if user_data: 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') if data.get('email_subscription', False): upsert_user.delay(SUBSCRIBE, { 'token': user_data['token'], 'lang': 'en-US', 'newsletters': 'mozilla-foundation', 'source_url': data['source_url'], }) campaign_member = { 'CampaignId': data['campaign_id'], 'ContactId': user_data['id'], 'Full_URL__c': data['source_url'], 'Status': 'Signed', } comments = data.get('comments') if comments: campaign_member['Petition_Comments__c'] = comments[:500] metadata = data.get('metadata') if metadata: campaign_member['Petition_Flex__c'] = json.dumps(metadata)[:500] try: sfdc.campaign_member.create(campaign_member) except sfapi.SalesforceMalformedRequest as e: if e.content and e.content[0].get('errorCode') == 'DUPLICATE_VALUE': # already in the system, ignore pass else: raise
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.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 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 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 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 record_common_voice_update(data): # do not change the sent data in place. A retry will use the changed data. dcopy = data.copy() email = dcopy.pop("email") user_data = get_user_data(email=email, extra_fields=["id"]) new_data = { "source_url": "https://voice.mozilla.org", "newsletters": [settings.COMMON_VOICE_NEWSLETTER], } for k, v in dcopy.items(): new_data["cv_" + k] = v if user_data: sfdc.update(user_data, new_data) else: new_data.update({"email": email, "token": generate_token()}) sfdc.add(new_data)
def record_common_voice_goals(data): email = data.pop('email') user_data = get_user_data(email=email, extra_fields=['id']) new_data = { 'source_url': 'https://voice.mozilla.org', 'newsletters': [settings.COMMON_VOICE_NEWSLETTER], } for k, v in data.items(): new_data['cv_' + k] = v if user_data: sfdc.update(user_data, new_data) else: new_data.update({ 'email': email, 'token': generate_token(), }) sfdc.add(new_data)
def fxa_email_changed(data): ts = data["ts"] fxa_id = data["uid"] email = data["email"] cache_key = "fxa_email_changed:%s" % fxa_id prev_ts = float(cache.get(cache_key, 0)) if prev_ts and prev_ts > ts: # message older than our last update for this UID return # Update SFDC / CTMS user_data = get_user_data(fxa_id=fxa_id, extra_fields=["id"]) if user_data: sfdc.update(user_data, {"fxa_primary_email": email}) ctms.update(user_data, {"fxa_primary_email": email}) else: # FxA record not found, try email user_data = get_user_data(email=email, extra_fields=["id"]) if user_data: sfdc.update(user_data, { "fxa_id": fxa_id, "fxa_primary_email": email }) ctms.update(user_data, { "fxa_id": fxa_id, "fxa_primary_email": email }) else: # No matching record for Email or FxA ID. Create one. data = { "email": email, "token": generate_token(), "fxa_id": fxa_id, "fxa_primary_email": email, } ctms_data = data.copy() contact = ctms.add(ctms_data) if contact: data["email_id"] = contact["email"]["email_id"] sfdc.add(data) statsd.incr("news.tasks.fxa_email_changed.user_not_found") cache.set(cache_key, ts, 7200) # 2 hr
def record_common_voice_goals(data): # do not change the sent data in place. A retry will use the changed data. dcopy = data.copy() email = dcopy.pop('email') user_data = get_user_data(email=email, extra_fields=['id']) new_data = { 'source_url': 'https://voice.mozilla.org', 'newsletters': [settings.COMMON_VOICE_NEWSLETTER], } for k, v in dcopy.items(): new_data['cv_' + k] = v if user_data: sfdc.update(user_data, new_data) else: new_data.update({ 'email': email, 'token': generate_token(), }) sfdc.add(new_data)
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'].items() 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: if nlid in nl_map: 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 process_donation(data): get_lock(data['email']) # tells the backend to leave the "subscriber" flag alone contact_data = {'_set_subscriber': False} # do "or ''" because data can contain None values first_name = (data.get('first_name') or '').strip() last_name = (data.get('last_name') or '').strip() if first_name and last_name: contact_data['first_name'] = first_name contact_data['last_name'] = last_name elif first_name: contact_data['first_name'] = first_name elif 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 # returns a dict with the new ID but no other user data, but that's enough here user_data = sfdc.add(contact_data) if not user_data.get('id'): # 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', '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', } # this is a unix timestamp in ms since epoc timestamp = data.get('created') if timestamp: donation['CloseDate'] = iso_format_unix_timestamp(timestamp) for dest_name, source_name in DONATION_NEW_FIELDS.items(): if source_name in data: donation[dest_name] = data[source_name] for dest_name, source_name in DONATION_OPTIONAL_FIELDS.items(): if data.get(source_name): # truncate at 2000 chars as that's the max for # a SFDC text field. We may do more granular # truncation per field in future. donation[dest_name] = data[source_name][:2000] try: sfdc.opportunity.create(donation) except sfapi.SalesforceMalformedRequest as e: if e.content and e.content[0].get('errorCode') == 'DUPLICATE_VALUE': # already in the system, ignore pass else: raise
def process_donation(data): get_lock(data['email']) # tells the backend to leave the "subscriber" flag alone contact_data = {'_set_subscriber': False} # do "or ''" because data can contain None values first_name = (data.get('first_name') or '').strip() last_name = (data.get('last_name') or '').strip() if first_name and last_name: contact_data['first_name'] = first_name contact_data['last_name'] = last_name elif first_name: contact_data['first_name'] = first_name elif 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', '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', } # this is a unix timestamp in ms since epoc timestamp = data.get('created') if timestamp: donation['CloseDate'] = iso_format_unix_timestamp(timestamp) for dest_name, source_name in DONATION_NEW_FIELDS.items(): if source_name in data: donation[dest_name] = data[source_name] for dest_name, source_name in DONATION_OPTIONAL_FIELDS.items(): if source_name in data: # truncate at 2000 chars as that's the max for # a SFDC text field. We may do more granular # truncation per field in future. donation[dest_name] = data[source_name][:2000] try: sfdc.opportunity.create(donation) except sfapi.SalesforceMalformedRequest as e: if e.content and e.content[0].get('errorCode') == 'DUPLICATE_VALUE': # already in the system, ignore pass else: raise
def test_valid_tokens(self): self.assertTrue(views.is_token('abcdef-abcdef-abcdef-deadbeef-123456')) self.assertTrue(views.is_token(utils.generate_token()))
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_acoustic_tx_messages( data["email"], data.get("lang", "en-US"), 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, ) send_confirm = False if api_call_type != UNSUBSCRIBE: # Check for newsletter-specific user updates to_subscribe_slugs = [ nl for nl, sub in update_data["newsletters"].items() if sub ] check_optin = not (forced_optin or (user_data and user_data.get("optin"))) check_mofo = not (user_data and user_data.get("mofo_relevant")) if to_subscribe_slugs and (check_optin or check_mofo): to_subscribe = Newsletter.objects.filter(slug__in=to_subscribe_slugs) # Are they subscribing to any newsletters that require confirmation? # If none require confirmation, user goes straight to confirmed (optin) # Otherwise, prepare to send a fx or moz confirmation if check_optin: exempt_from_confirmation = any( [not o.requires_double_optin for o in to_subscribe], ) if exempt_from_confirmation: update_data["optin"] = True else: send_fx_confirm = all([o.firefox_confirm for o in to_subscribe]) send_confirm = "fx" if send_fx_confirm else "moz" # Update a user to MoFo-relevant if they subscribed to a MoFo newsletters if check_mofo: if any([ns.is_mofo for ns in to_subscribe]): update_data["mofo_relevant"] = True if user_data is None: # no user found. create new one. token = update_data["token"] = generate_token() if settings.MAINTENANCE_MODE: sfdc_add_update.delay(update_data) else: ctms_data = update_data.copy() ctms_contact = ctms.add(ctms_data) if ctms_contact: # Successfully added to CTMS, send email_id to SFDC update_data["email_id"] = ctms_contact["email"]["email_id"] # don't catch exceptions here. SalesforceError subclasses will retry. sfdc.add(update_data) if send_confirm and settings.SEND_CONFIRM_MESSAGES: send_confirm_message.delay( data["email"], token, data.get("lang", "en-US"), send_confirm, ) return 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) ctms.update(user_data, update_data) if send_confirm and settings.SEND_CONFIRM_MESSAGES: send_confirm_message.delay( user_data["email"], token, update_data.get("lang", user_data.get("lang", "en-US")), send_confirm, ) return token, False
def process_petition_signature(data): """ Add petition signature to CTMS / SFDC If SFDC is enabled, a campaign member record is created. """ data = data["form"] get_lock(data["email"]) # tells the backend to leave the "subscriber" flag alone contact_data = { "_set_subscriber": False, # SFDC: leave the "subscriber" flag alone "mofo_relevant": True, # CTMS: set contact as MoFo relevant } contact_data.update({k: data[k] for k in PETITION_CONTACT_FIELDS if data.get(k)}) user_data = get_user_data(email=data["email"], extra_fields=["id"]) if user_data: sfdc.update(user_data, contact_data) ctms_data = contact_data.copy() del ctms_data["_set_subscriber"] ctms.update(user_data, ctms_data) else: contact_data["token"] = generate_token() contact_data["email"] = data["email"] ctms_data = contact_data.copy() contact_data["record_type"] = settings.DONATE_CONTACT_RECORD_TYPE del ctms_data["_set_subscriber"] ctms_data["mofo_relevant"] = True contact = ctms.add(ctms_data) if contact: contact_data["email_id"] = contact["email"]["email_id"] 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") if data.get("email_subscription", False): upsert_user.delay( SUBSCRIBE, { "token": user_data["token"], "lang": data.get("lang", "en-US"), "newsletters": "mozilla-foundation", "source_url": data["source_url"], }, ) if not settings.SFDC_ENABLED: return campaign_id = data["campaign_id"] # Fix a specific issue with a specific campaign where the ID was entered without # the leading 7 if len(campaign_id) == 17 and not campaign_id.startswith("7"): campaign_id = f"7{campaign_id}" campaign_member = { "CampaignId": campaign_id, "ContactId": user_data["id"], "Full_URL__c": data["source_url"], "Status": "Signed", } comments = data.get("comments") if comments: campaign_member["Petition_Comments__c"] = comments[:500] metadata = data.get("metadata") if metadata: campaign_member["Petition_Flex__c"] = json.dumps(metadata)[:500] try: sfdc.campaign_member.create(campaign_member) except sfapi.SalesforceMalformedRequest as e: if e.content and e.content[0].get("errorCode") == "DUPLICATE_VALUE": # already in the system, ignore pass else: raise
def process_donation(data): if 'data' in data: # here for old messages # TODO remove in a subsequent deployment data = data['data'] get_lock(data['email']) # tells the backend to leave the "subscriber" flag alone contact_data = {'_set_subscriber': False} first_name = data.get('first_name', '').strip() last_name = data.get('last_name', '').strip() if first_name and last_name: contact_data['first_name'] = first_name contact_data['last_name'] = last_name elif first_name: contact_data['first_name'] = first_name elif 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', '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', } # this is a unix timestamp in ms since epoc timestamp = data.get('created') if timestamp: donation['CloseDate'] = iso_format_unix_timestamp(timestamp) for dest_name, source_name in DONATION_OPTIONAL_FIELDS.items(): if source_name in data: donation[dest_name] = data[source_name] try: sfdc.opportunity.create(donation) except sfapi.SalesforceMalformedRequest as e: if e.content and e.content[0].get('errorCode') == 'DUPLICATE_VALUE': # already in the system, ignore pass else: raise
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: if nlid in nl_map: 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 process_donation(data): get_lock(data["email"]) contact_data = { "_set_subscriber": False, # SFDC, leave "subscriber" flag alone "mofo_relevant": True, # CTMS, set a MoFo relevant contact } # do "or ''" because data can contain None values first_name = (data.get("first_name") or "").strip() last_name = (data.get("last_name") or "").strip() if first_name and last_name: contact_data["first_name"] = first_name contact_data["last_name"] = last_name elif first_name: contact_data["first_name"] = first_name elif 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) ctms_data = contact_data.copy() del ctms_data["_set_subscriber"] ctms.update(user_data, ctms_data) else: contact_data["token"] = generate_token() contact_data["email"] = data["email"] contact_data["record_type"] = settings.DONATE_CONTACT_RECORD_TYPE ctms_data = contact_data.copy() del ctms_data["_set_subscriber"] del ctms_data["record_type"] contact = ctms.add(ctms_data) if contact: contact_data["email_id"] = contact["email"]["email_id"] if not settings.SFDC_ENABLED: return # returns a dict with the new ID but no other user data, but that's enough here user_data = sfdc.add(contact_data) if not user_data.get("id"): # retry here to make sure we associate the donation data with the proper account raise RetryTask("User not yet available") if not settings.SFDC_ENABLED: return # add opportunity donation = { "RecordTypeId": settings.DONATE_OPP_RECORD_TYPE, "Name": "Foundation Donation", "Donation_Contact__c": user_data["id"], "StageName": "Closed Won", "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", } # https://github.com/mozmeao/basket/issues/364 if "campaign_id" in data: donation["CampaignId"] = data["campaign_id"] # this is a unix timestamp in ms since epoc timestamp = data.get("created") if timestamp: donation["CloseDate"] = iso_format_unix_timestamp(timestamp) for dest_name, source_name in DONATION_NEW_FIELDS.items(): if source_name in data: donation[dest_name] = data[source_name] for dest_name, source_name in DONATION_OPTIONAL_FIELDS.items(): if data.get(source_name): # truncate at 2000 chars as that's the max for # a SFDC text field. We may do more granular # truncation per field in future. donation[dest_name] = data[source_name][:2000] try: sfdc.opportunity.create(donation) except sfapi.SalesforceMalformedRequest as e: if e.content and e.content[0].get("errorCode") == "DUPLICATE_VALUE": # already in the system, ignore pass else: raise
def test_valid_tokens(self): self.assertTrue(views.is_token('abcdef-abcdef-abcdef-deadbeef-123456')) self.assertTrue(views.is_token(utils.generate_token()))