def sfdc_add_update(update_data, user_data=None): # for use with maintenance mode only # TODO remove after maintenance is over and queue is processed if user_data: sfdc.update(user_data, update_data) ctms.update(user_data, update_data) else: ctms_data = update_data.copy() ctms_contact = ctms.add(ctms_data) if ctms_contact: update_data["email_id"] = ctms_contact["email"]["email_id"] try: sfdc.add(update_data) except sfapi.SalesforceMalformedRequest as e: # noqa # possibly a duplicate email. try the update below. user_data = get_user_data(email=update_data["email"], extra_fields=["id"]) if user_data: # we have a user, delete generated token and email_id # and continue with an update update_data.pop("token", None) update_data.pop("email_id", None) sfdc.update(user_data, update_data) ctms.update(user_data, update_data) else: # still no user, try the add one more time ctms_contact = ctms.add(update_data) if ctms_contact: update_data["email_id"] = ctms_contact["email"]["email_id"] sfdc.add(update_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_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) ctms.update(user_data, new_data) else: new_data.update({"email": email, "token": generate_token()}) ctms_data = new_data.copy() ctms_contact = ctms.add(ctms_data) if ctms_contact: new_data["email_id"] = ctms_contact["email"]["email_id"] sfdc.add(new_data)
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 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