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]>