Profile bulk handler

Retrieving the bulk handler

class ProfileBulkhandlerMixin
get_profile_bulkhandler(feedback=False, matching_properties=None, create_profiles=False, strategy=None, callback=None)

Perform bulk “update” and/or “insert” operations on profiles. This functions returns a BulkHandler object that can be used to perform the bulk operations.

Parameters:
  • feedback (bool) – If set to True feedback will be shown when profiles have been written to BlueConic. Note that this only reflects changes to direct profile properties, changes to nested properties are ignored. Defaults to False.

  • matching_properties (Sequence[Union[str, ProfileProperty]]) – The profile properties to match on

  • create_profiles (bool) – Create new profiles if profile is not found. Deprecated in favor of using strategy.

  • strategy (BulkStrategy) – Strategy, either UPSERT (create or update), UPDATE or DELETE

  • callback (Callable) – Callback to perform when REST response is received

Returns:

BulkHandler for profiles

Return type:

BulkHandler

Usage:
>>> import blueconic
>>> bc = blueconic.Client()
>>> def print_bulk_response(bulk_response):
>>>    # this returns the profile counts
>>>    print(bulk_response.counts)
>>>    # {'MODIFIED': 10, 'CREATED': 20, 'UNCHANGED': 1}
>>>    print(bulk_response.timeline_event_counts)
>>>    # {'REJECTED': 1, 'MODIFIED': 3, 'SET': 1, 'NOTFOUND': 5, 'CREATED': 2, 'DELETED': 1}
>>>    for result in bulk_response.results:
>>>         print(result.profile_id)
>>>         # c3e6278e-5073-4407-aec8-fe986cefe0df
>>>         print(result.state)
>>>         # CREATED, MODIFIED etc
>>>         print(result.identifier)
>>>         # the external identifier
>>>         print(result.validation_errors)
>>>         # map of validation errors per profile property
>>>         print(result.timeline_events)
>>>         # list of timeline event operation updates
>>>
>>>       for timeline in result.timeline_events:
>>>           print(timeline.event_id)
>>>           # order_event_1
>>>           print(timeline.identifier)
>>>           # the external identifier. I.E. order_id
>>>           print(timeline.state)
>>>           # REJECTED, CREATED, MODIFIED, NOTFOUND, DELETED etc
>>>           print(timeline.validation_errors)
>>>           # list of validation errors for this timeline event
>>>           # ['#/totalAmount/0: expected type: Integer, found: String']
>>>
>>> segment_id = bc.get_blueconic_parameter_value("My segment","segment")
>>> with bc.get_profile_bulkhandler(feedback=True, callback=print_bulk_response) as pbh:
>>>     for profile in bc.get_profiles(segment_id, count=3):
>>>         profile.set_value('next_best_product', 'iphone')
>>>         pbh.write(profile)
[{'profileId': 'c3e6278e-5073-4407-aec8-fe986cefe0df', 'state': 'MODIFIED'},
{'profileId': 'd3a60b4f-a33d-4a0e-b508-e6a9ce2e4fde', 'state': 'MODIFIED'},
{'profileId': '70526349-7705-41e0-934e-c570ca3d5611', 'state': 'MODIFIED'}]

Using the bulk handler

class BulkHandler
flush()

Closes the stream and writes the changes to BlueConic.

open_stream()

Open a stream to BlueConic.

write(writable_object)

Writes the changes in a bulkhandler object back to BlueConic.

Parameters:

writable_object (ProfileOperation, Profile (deprecated), Group) – The object to be updated in BlueConic

Raises:
  • ValueError – The temporary file is not open.

  • TypeError – The variable ‘writable_object’ is not of type HashMap.

Using ProfileOperation with bulk handler

Using ProfileOperation objects is the preferred way of doing updates on profiles. It’s possible to use profile_id or matching_properties to find the profile that needs to be updated. If multiple matching properties are passed, the profile must match the value for at least one of the given properties (there is an implicit OR relation).

blueconic.Client().get_profile_bulkhandler() allows to provide matching_properties as well, but if matching_properties are used in ProfileOperation, then matching_properties provided to blueconic.Client().get_profile_bulkhandler() are ignored.

>>> import blueconic
>>> bc = blueconic.Client()
>>> operation = blueconic.ProfileOperation(profile_id="0cf7e3a1-fa2c-43e5-ae4c-c648724bf1af")
>>> operation.add_rule("currentbrowsername", "Safari", blueconic.Strategy.SET)
>>> operation.add_rule("name", "John", blueconic.Strategy.SET_IF_EMPTY)
>>> with bc.get_profile_bulkhandler() as bh:
>>>     bh.write(operation)
>>> import blueconic
>>> bc = blueconic.Client()
>>> matching_props = [{"email" :"example@example.com"}]
>>> # matching_props = [{"email" :"example@example.com"}, {"property_id": "property_value"}]
>>> operation = blueconic.ProfileOperation(matching_properties=matching_props)
>>> operation.add_rule("currentbrowsername", "Safari", blueconic.Strategy.SET)
>>> operation.add_rule("name", "John", blueconic.Strategy.SET_IF_EMPTY)
>>> with bc.get_profile_bulkhandler() as bh:
>>>     bh.write(operation)

One can use all strategies defined in Strategy.

class Strategy

This enumeration defines all possible strategies for rules for profile properties and individual timeline event properties.

SET:

Replaces existing values with the given values.

ADD:

Adds given values to existing values.

SET_IF_EMPTY:

Only replaces the existing values with the given values when there are no existing values.

SUM:

For number properties, sums up the existing value with the given value.

INCREMENT:

For number properties, increments up the existing value with the given value.

REMOVE:

Removes given values from the existing values.

Using TimelineEventOperation with bulk handler

By using TimelineEventOperation objects is the preferred way of doing (partial) updates on timeline events. TimelineEventOperation can be used to set or update individual timeline event properties and TimelineNestedItemOperation can be used to set or update nested properties (e.g. a product in products) of a timeline event.

>>> import blueconic
>>> from datetime import datetime
>>> bc = blueconic.Client()
>>> operation = blueconic.ProfileOperation(profile_id="0cf7e3a1-fa2c-43e5-ae4c-c648724bf1af")
>>>
>>> # Perform UPSERT on TimelineEvent with type "abandoned_basket" with id "abandoned_basket_2"
>>> timeline_operation = blueconic.TimelineEventOperation(
>>>    "abandoned_basket", blueconic.TimelineStrategy.UPSERT, event_id="abandoned_basket_2"
>>> )
>>> # Root-level properties
>>> timeline_operation.add_rule("total_value", 100, blueconic.Strategy.SET)
>>> timeline_operation.add_rule("last_modified", datetime.now(), blueconic.Strategy.SET)
>>>
>>> # Nested-properties
>>> # Perform operations on nested property with identifier property "id" and value "987644321"
>>> nested_property_operation = blueconic.TimelineNestedItemOperation("id", "987644321")
>>> nested_property_operation.add_rule("quantity", 5, blueconic.Strategy.SET)
>>> nested_property_operation.add_rule("netprice", 19.99, blueconic.Strategy.SET)
>>> nested_property_operation.add_rule("variant", ["variant1", "variant2"], blueconic.Strategy.SET)
>>> nested_property_operation.add_rule("variant", ["variant3", "variant4"], blueconic.Strategy.ADD)
>>> nested_property_operation.add_rule("last_modified", datetime.now(), blueconic.Strategy.SET_IF_EMPTY)
>>>
>>> # Perform nested property operation on root level property "product"
>>> timeline_operation.add_rule("product", nested_property_operation, blueconic.TimelineStrategy.UPSERT)
>>>
>>> # Add TimelineEventOperation to the ProfileOperation
>>> operation.add_timeline_operation(timeline_operation)
>>> with bc.get_profile_bulkhandler() as bh:
>>>     bh.write(operation)

One can use all strategies for individual timeline event properties as defined in Strategy.

class Strategy

This enumeration defines all possible strategies for rules for profile properties and individual timeline event properties.

SET:

Replaces existing values with the given values.

ADD:

Adds given values to existing values.

SET_IF_EMPTY:

Only replaces the existing values with the given values when there are no existing values.

SUM:

For number properties, sums up the existing value with the given value.

INCREMENT:

For number properties, increments up the existing value with the given value.

REMOVE:

Removes given values from the existing values.

One can use all strategies for the entire timeline event properties or nested properties as defined in TimelineStrategy.

class TimelineStrategy

This enumeration defines all possible strategies for timeline events. This defines the strategy for the entire event and nested properties of a timeline event (not for the individual timeline event properties).

SET:

Default. Indicates an upsert for the timeline event. If the event already exists it is overwritten (updated), otherwise inserted. The strategy set on individual properties will be ignored.

UPSERT:

Indicates a partial update (upsert). If the event is not found it is equal to a SET, otherwise the strategy set on individual properties will determine how the update is performed.

UPDATE:

Indicates a partial update. If the event is not found, nothing will happen, otherwise the strategy set on individual properties will determine how the update is performed.

DELETE:

Indicates a delete. Will delete the given event or item in a nested property.

Using callback with Profile bulk handler

It’s possible to provide a callback function to blueconic.Client().get_profile_bulkhandler(). The callback function accepts a single parameter. The callback will be called once the bulk handler receives response. The response will be parsed into blueconic.domain.ProfileBulkResponse() and passed to callback function.

>>> import blueconic
>>> bc = blueconic.Client()
>>>
>>> def print_bulk_response(bulk_response):
>>>    # this returns the profile counts
>>>    print(bulk_response.counts)
>>>    # {'MODIFIED': 10, 'CREATED': 20, 'UNCHANGED': 1}
>>>    print(bulk_response.timeline_event_counts)
>>>    # {'REJECTED': 1, 'MODIFIED': 3, 'SET': 1, 'NOTFOUND': 5, 'CREATED': 2}
>>>    for result in bulk_response.results:
>>>         print(result.profile_id)
>>>         # c3e6278e-5073-4407-aec8-fe986cefe0df
>>>         print(result.state)
>>>         # CREATED, MODIFIED etc
>>>         print(result.identifier)
>>>         # the external identifier
>>>         print(result.validation_errors)
>>>         # map of validation errors per profile property
>>>         print(result.timeline_events)
>>>         # list of timeline event operation updates
>>>
>>>       for timeline in result.timeline_events:
>>>           print(timeline.event_id)
>>>           # order_event_1
>>>           print(timeline.identifier)
>>>           # the external identifier. I.E. order_id
>>>           print(timeline.state)
>>>           # REJECTED, CREATED, MODIFIED, NOTFOUND etc
>>>           print(timeline.validation_errors)
>>>           # ['#/totalAmount/0: expected type: Integer, found: String']
>>>
>>> segment_id = bc.get_blueconic_parameter_value("My segment","segment")
>>> with bc.get_profile_bulkhandler(callback=print_bulk_response) as pbh:
>>>     for profile in bc.get_profiles(segment_id, count=3):
>>>         profile.set_value('next_best_product', 'iphone')
>>>         pbh.write(profile)

Using the old API of doing profile updates (deprecated)

Before the “operation” objects were introduced, the only way to do updates on a profile (or it’s timeline) was to use the add_value/set_value/add_timeline_event methods on a profile object. This is still possible, but it’s superseded by the “operations” API described above.

Importing data from an external source

The bulk handler can also be used to create or update profiles without retrieving them from BlueConic first. This allows to more efficiently import data from an external data source. For example, the following code would import customer data into BlueConic, updating a profile if the given customer_id exists, and otherwise creating a new profile.

>>> from blueconic import Client, Profile
>>> bc = Client()
>>> with bc.get_profile_bulkhandler(matching_properties=["customer_id"], create_profiles=True) as handler:
>>>     for customer in customer_data:
>>>         profile = Profile()
>>>         profile.set_value("customer_id", customer["customer_id"])
>>>         profile.set_value("name", customer["name"])
>>>         profile.add_values("orders", customer["orders"])
>>>         handler.write(profile)

The matching_properties must exist and have the “Is unique identifier” flag set. If multiple properties are passed, the existing profile must match the value for at least one of the given properties. BlueConic will use the first match based on the order in which the matching properties are passed to the bulk handler.

If create_profiles=False is passed, and an existing profile cannot be found, the data is ignored.

With statement

The with statement simplifies the writing and ensures that clean-up code is executed. It is possible to write profiles to BlueConic without the with statement. The following code block provides such an example.

>>> import blueconic
>>> bc = blueconic.Client()
>>> bulkhandler = bc.get_profile_bulkhandler(feedback=True)
>>> bulkhandler.open_stream()
>>> example_profile.add_value('next_best_product', 'iphone')
>>> bulkhandler.write(example_profile)
>>> bulkhandler.flush()
[{'profileId': '5971efee-d07f-4cf9-b870-ff4b16ea2a73', 'state': 'MODIFIED'}]
<Response [200]>