Using the BlueConic Python Package

The BlueConic Python package is the cornerstone of building custom notebooks in AI Workbench.  Using it will open up endless possibilities to analyze and optimize your BlueConic profile data.

 

Code Snippet 1: Set up a basic 'get_profiles' loop

This first code snippet implements a basic loop to process BlueConic profiles. Using the count parameter of the get_profiles method makes sure that only the first 5,000 profiles of the "All visitors" segment are processed. Processing all profiles in this segment could potentially take a very long time to execute.

# Import the BlueConic client
import blueconic
bc = blueconic.Client()

# Counter to keep track of the profiles with one or more email addresses
nr_profiles = 0

# Loop over first 5,000 profiles in the "All Visitors" segment
for profile in bc.get_profiles(segment_id="allvisitors",
                               count=5000):
   
    # Retrieve the value of the "email" profile property
    email_address = profile.get_value("email")
    
    # When there is a value present for the email address, increase the counter
    if email_address:
        nr_profiles += 1
        if nr_profiles <= 10:
            print(f"Email address {nr_profiles}: {email_address}")

# Print the result
print(f"Profiles with an email address: {nr_profiles}")


Code Snippet 2: Only retrieve profiles with an Email Address and only ask for the Email Address property

The second code snippet resolves a few issues from example 1:

  • In the first snippet, all the data in each profile is returned, including data we don't need for our notebook. In this example, we add the properties parameter to the get_profiles method so only the values for the listed properties (in this case the "email" property) are returned.
  • In the first code snippet, the first 5,000 profiles of the "All visitors" segment are returned, including profiles that have no value for the profile property that we want to analyze. By using the required_properties parameter for the get_profiles method, profiles are returned only if they have one or more values for the listed profile properties (in our example, profiles with values for a property with the id "email").
  • In the first snippet, the count was set to 5,000 to prevent a long execution time. Now that we've made sure that only relevant profiles are returned (by using the required_properties parameter), we set the count to zero (0), which means that all profiles will be returned that are part of the segment with the id "allvisitors" and have a value for the profile property with the id "email". Note: This could still lead to long execution times when you have a lot of profiles with a value for the listed required_properties.
# Import the BlueConic client
import blueconic
bc = blueconic.Client()

# Counter to keep track of the profiles with one or more email addresses
nr_profiles = 0

# Loop over all profiles in the "All Visitors" segment where there is
# a value present for the "email" profile property.
# Also only return the "email" property for each profile.
for profile in bc.get_profiles(segment_id="allvisitors",
                               required_properties=["email"],
                               properties=["email"],
                               count=0):
       
    # Increase the 'number of profiles with one or more values' counter
    nr_profiles += 1

# Print the result
print(f"Profiles with an email address: {nr_profiles}")


Code Snippet 3: Enrich profiles using the profile bulk handler

Code snippet 2 loops over all profiles that have one or more email addresses. In this example, the number of email addresses within a profile is written back to a new profile property (with the id "nr_email_addresses") in the same profile. Profiles can only be updated by using the profile bulk handler.

Note: This example requires that you have a BlueConic profile property with the id "nr_email_addresses". Create this profile property before running the code example.
# Import the BlueConic client
import blueconic
bc = blueconic.Client()

# Counter to keep track of the profiles with one or more email addresses
nr_profiles = 0

# Retrieve the profile bulk handler to update profiles in bulk
with bc.get_profile_bulkhandler() as pbh:
    
    # Loop over all profiles in the "All Visitors" segment where there is
    # a value present for the "email" profile property.
    # Also only return the "email" property for each profile.
    for profile in bc.get_profiles(segment_id="allvisitors",
                                   properties=["email"],
                                   required_properties=["email"],
                                   count=0):
    
        # Retrieve the number of values in "email" profile property
        nr_email_addresses = len(profile.get_values("email"))

        # Write the number of email addresses to the "nr_email_addresses" property
        profile.set_value("nr_email_addresses", nr_email_addresses)
        
        # Write the updated profile back to BlueConic
        pbh.write(profile)

        # Increase the 'number of profiles with one or more values' counter
        nr_profiles += 1     

# Print the result
print(f"Updated {nr_profiles} profiles")


Code Snippet 4: Use the Parameters feature to retrieve the segment and profile properties

In snippet 3, the ids of the BlueConic items that are needed are placed in the source code of the notebook (for example: the profile property id "email" that refers to the "Email Address" profile property). This is not ideal because sometimes as a developer you won't know the id of a needed item.

Using the Parameters feature of the BlueConic AI Workbench solves this issue. Here's how it works:

  • In the source code, retrieve the value for a parameter of a certain type, using the get_blueconic_parameter_value or get_blueconic_parameter_values method.
  • All parameters from the notebook code are automatically listed in the left navigation menu of the AI Workbench "Parameters" tab.
  • Marketers can enter a value for each parameter using the UI.
  • When you run the notebook code, the value(s) marketers enter for the parameters are inserted into the notebook.
# Import the BlueConic client
import blueconic
bc = blueconic.Client()

# "segment" parameter: The segment to process the profiles from
source_segment_id = bc.get_blueconic_parameter_value("Segment to process","segment")

# "profile_property" parameter: Count the number of values this profile property contains
source_property_id = bc.get_blueconic_parameter_value("Source Property", "profile_property")

# "profile_property" parameter: Store the value count of source_property_id into this property
target_property_id = bc.get_blueconic_parameter_value("Target Property", "profile_property")

# Counter to keep track of the profiles with one or more values
nr_profiles = 0

# Retrieve the profile bulk handler to update profiles in bulk
with bc.get_profile_bulkhandler() as pbh:
    # Loop over all profiles in source_segment_id where there is a value present in source_property_id
    for profile in bc.get_profiles(segment_id=source_segment_id,
                               properties=[source_property_id],
                               required_properties=[source_property_id],
                               count=0):
    
        # Retrieve the number of values of the source_property_id
        nr_email_addresses = len(profile.get_values(source_property_id))

        # Write the number of values present in source_property_id to target_property_id
        profile.set_value(target_property_id, nr_email_addresses)
        
        # Write the updated profile back to BlueConic
        pbh.write(profile)

        # Increase the 'number of profiles with one or more values' counter
        nr_profiles += 1     

# Print the result
print(f"Updated {nr_profiles} profiles")


Code Snippet 5: Update only the profiles that have changed values since the last run

Snippet 4 produces the correct results, but can be further optimized for performance. Once we have determined the number of values in a profile property (and wrote the result back to another property in the same profile), this notebook doesn't have to analyze a profile again if it has not been changed since the last successful run of this notebook, or if the value for the Number of Email Addresses profile property didn't change.

To make this happen, we add the following changes to snippet 4:

  • Add a fourth profile property parameter that contains the moment of the last update to the profile.
  • Create a function that returns the date of the last successful execution of this notebook.
  • Create a filter that will leave out all profiles that have not been updated since the last successful execution of this notebook.
  • Apply the filter through the filters parameter when retrieving profiles with the get_profiles method.
  • Let get_profiles also return the value for the Number of Email Addresses profile property, and use this value to implement a check so only profiles are updated that have a changed value for this property.
# Import the BlueConic client
import blueconic
bc = blueconic.Client()

# "segment" parameter: The segment to process the profiles from
source_segment_id = bc.get_blueconic_parameter_value("Segment to process","segment")

# "profile_property" parameter: Count the number of values this profile property contains
source_property_id = bc.get_blueconic_parameter_value("Source Property", "profile_property")

# "profile_property" parameter: Store the value count of source_property_id into this property
target_property_id = bc.get_blueconic_parameter_value("Target Property", "profile_property")

# "profile_property" parameter: Last modification date of the profile
source_last_modified_date = bc.get_blueconic_parameter_value("Last modification date", "profile_property")


# Returns the start date of the last successful execution of this notebook
def get_last_successful_execution_start_date():
    for execution in bc.get_executions(count=10):
        if execution.state == "FINISHED":
            return execution.start_date
    return None

# Use the last successful execution of this notebook to add a filter based
# on the "lastmodifieddate" profile property. This filter will make sure
# that only profiles that have been updated since the last successful run
# will be processed again by this notebook.
all_filters = []
last_successful_execution_start_date = get_last_successful_execution_start_date()
if last_successful_execution_start_date:
    lastmodified_date_filter = blueconic.get_filter(source_last_modified_date).in_range(min=last_successful_execution_start_date)
    all_filters = [lastmodified_date_filter]


# Counter to keep track of the profiles with one or more values
nr_profiles = 0

# Retrieve the profile bulk handler to update profiles in bulk
with bc.get_profile_bulkhandler() as pbh:
    
    # Loop over all profiles in source_segment_id where there is a value present in source_property_id
    for profile in bc.get_profiles(segment_id=source_segment_id,
                                properties=[source_property_id, target_property_id],
                                required_properties=[source_property_id],
                                count=0,
                                filters=all_filters):
    
        # Retrieve the number of values of the source_property_id
        nr_email_addresses = len(profile.get_values(source_property_id))

        # Check if the number of values in the property has changed
        if nr_email_addresses != profile.get_value(target_property_id):

            # Write the number of values present in source_property_id to target_property_id
            profile.set_value(target_property_id, nr_email_addresses)
        
            # Write the updated profile back to BlueConic
            pbh.write(profile)

            # Increase the 'number of profiles with one or more values' counter
            nr_profiles += 1     

# Print the result
print(f"Updated {nr_profiles} profiles")