facebook_business/adobjects/serverside/custom_data.py (247 lines of code) (raw):
# Copyright 2014 Facebook, Inc.
# You are hereby granted a non-exclusive, worldwide, royalty-free license to
# use, copy, modify, and distribute this software in source code or binary
# form for use in connection with the web services and APIs provided by
# Facebook.
# As with any software that integrates with the Facebook platform, your use
# of this software is subject to the Facebook Developer Principles and
# Policies [http://developers.facebook.com/policy/]. This copyright notice
# shall be included in all copies or substantial portions of the software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
import pprint
import six
from facebook_business.adobjects.serverside.content import Content
from facebook_business.adobjects.serverside.normalize import Normalize
from facebook_business.adobjects.serverside.delivery_category import DeliveryCategory
class CustomData(object):
"""
CustomData includes additional business data about the event.
"""
param_types = {
'value': 'float',
'currency': 'str',
'content_name': 'str',
'content_category': 'str',
'content_ids': 'list[str]',
'contents': 'list[Content]',
'content_type': 'str',
'order_id': 'str',
'predicted_ltv': 'float',
'num_items': 'int',
'status': 'str',
'search_string' : 'str',
'delivery_category' : 'DeliveryCategory',
'item_number': 'str',
'custom_properties' : 'dict'
}
def __init__(
self,
value=None,
currency=None,
content_name=None,
content_category=None,
content_ids=None,
contents=None,
content_type=None,
order_id=None,
predicted_ltv=None,
num_items=None,
status=None,
search_string=None,
delivery_category=None,
item_number=None,
custom_properties={},
):
# type: (float, str, str, str, content_ids, List[str], List[Content], str, str, float, int, str, str, DeliveryCategory ,str, dict) -> None
self._value = None
self._currency = None
self._content_name = None
self._content_category = None
self._content_ids = None
self._contents = None
self._content_type = None
self._order_id = None
self._predicted_ltv = None
self._num_items = None
self._status = None
self._search_string = None
self._delivery_category = None
self._item_number = None
self._custom_properties = None
if value is not None:
self.value = value
if currency is not None:
self.currency = currency
if content_name is not None:
self.content_name = content_name
if content_category is not None:
self.content_category = content_category
if content_ids is not None:
self.content_ids = content_ids
if contents is not None:
self.contents = contents
if content_type is not None:
self.content_type = content_type
if order_id is not None:
self.order_id = order_id
if predicted_ltv is not None:
self.predicted_ltv = predicted_ltv
if num_items is not None:
self.num_items = num_items
if status is not None:
self.status = status
if search_string is not None:
self.search_string = search_string
if delivery_category is not None:
self.delivery_category = delivery_category
if item_number is not None:
self.item_number = item_number
if custom_properties:
self.custom_properties = custom_properties
@property
def value(self):
"""Gets the value.
A numeric value associated with this event. This could be a monetary value or a value in some other metric.
:return: The value.
:rtype: float or int
"""
return self._value
@value.setter
def value(self, value):
"""Sets the value.
A numeric value associated with this event. This could be a monetary value or a value in some other metric.
:param value: The value.
:type: float or int
"""
if not isinstance(value, (float, int)):
raise TypeError('CustomData.value must be a float or int. TypeError on value: %s' % value)
self._value = value
@property
def currency(self):
"""Gets the currency.
The currency for the value specified, if applicable. Currency must be a valid ISO 4217 three digit currency code.
:return: The currency.
:rtype: str
"""
return self._currency
@currency.setter
def currency(self, currency):
"""Sets the currency.
The currency for the value specified, if applicable. Currency must be a valid ISO 4217 three digit currency code.
:param currency: The currency.
:type: str
"""
self._currency = currency
@property
def content_name(self):
"""Gets the content name.
The name of the page or product associated with the event. Example: 'lettuce'.
:return: The content name.
:rtype: str
"""
return self._content_name
@content_name.setter
def content_name(self, content_name):
"""Sets the content name.
The name of the page or product associated with the event. Example: 'lettuce'.
:param content_name: The content name.
:type: str
"""
self._content_name = content_name
@property
def content_category(self):
"""Gets the content category.
The category of the content associated with the event. Example: 'grocery'
:return: The content category.
:rtype: str
"""
return self._content_category
@content_category.setter
def content_category(self, content_category):
"""Sets the content category.
The category of the content associated with the event. Example: 'grocery'
:param content_category: The content category.
:type: str
"""
self._content_category = content_category
@property
def content_ids(self):
"""Gets the content ids.
The content IDs associated with the event, such as product SKUs for items in an
AddToCart event: ['ABC123', 'XYZ789']. If content_type is a product, then your content IDs must
be an array with a single string value. Otherwise, this array can contain any number of string values.
:return: The content ids.
:rtype: list[str]
"""
return self._content_ids
@content_ids.setter
def content_ids(self, content_ids):
"""Sets the content_ids.
The content IDs associated with the event, such as product SKUs for items in an AddToCart event:
['ABC123', 'XYZ789']. If content_type is a product, then your content IDs must be an array with
a single string value. Otherwise, this array can contain any number of string values.
:param content_ids: The content_ids.
:type: list[str]
"""
self._content_ids = content_ids
@property
def contents(self):
"""Gets the contents.
A list of Content objects that contain the product IDs associated with the event plus information about
the products. id, quantity, and item_price are available fields.
:return: The contents.
:rtype: list[Content]
"""
return self._contents
@contents.setter
def contents(self, contents):
"""Sets the contents.
A list of Content objects that contain the product IDs associated with the event plus information about
the products. id, quantity, and item_price are available fields.
:param contents: The contents.
:type: list[Content]
"""
self._contents = contents
@property
def content_type(self):
"""Gets the content type.
A String equal to either 'product' or 'product_group'. Set to product if the keys you send content_ids or
contents represent products. Set to product_group if the keys you send in content_ids represent product groups.
:return: The content type.
:rtype: str
"""
return self._content_type
@content_type.setter
def content_type(self, content_type):
"""Sets the content type.
A String equal to either 'product' or 'product_group'. Set to product if the keys you send content_ids or
contents represent products. Set to product_group if the keys you send in content_ids represent product groups.
:param content_type: The content type.
:type: str
"""
self._content_type = content_type
@property
def order_id(self):
"""Gets the order id.
The order ID for this transaction as a String.
:return: The order id.
:rtype: str
"""
return self._order_id
@order_id.setter
def order_id(self, order_id):
"""Sets the order id.
The order ID for this transaction as a String.
:param order_id: The order id.
:type: str
"""
self._order_id = order_id
@property
def predicted_ltv(self):
"""Gets the predicted ltv.
The predicted lifetime value of a conversion event
:return: The predicted ltv.
:rtype: float or int
"""
return self._predicted_ltv
@predicted_ltv.setter
def predicted_ltv(self, predicted_ltv):
"""Sets the predicted_ltv.
The predicted lifetime value of a conversion event
:param predicted_ltv: The predicted_ltv.
:type: float or int
"""
if not isinstance(predicted_ltv, (float, int)):
raise TypeError('CustomData.predicted_ltv must be a float or int. TypeError on predicted_ltv: %s' % predicted_ltv)
self._predicted_ltv = predicted_ltv
@property
def num_items(self):
"""Gets the num items.
Use only with InitiateCheckout events. The number of items that a user tries to buy during checkout.
:return: The num_items.
:rtype: int
"""
return self._num_items
@num_items.setter
def num_items(self, num_items):
"""Sets the num items.
Use only with InitiateCheckout events. The number of items that a user tries to buy during checkout.
:param num_items: The num_items.
:type: int
"""
self._num_items = num_items
@property
def status(self):
"""Gets the status.
Use only with CompleteRegistration events. The status of the registration event, as a String.
:return: The status.
:rtype: str
"""
return self._status
@status.setter
def status(self, status):
"""Sets the status.
Use only with CompleteRegistration events. The status of the registration event, as a String.
:param status: The status.
:type: str
"""
self._status = status
@property
def search_string(self):
"""Gets the search query made by a user.
:return: The search query.
:rtype: str
"""
return self._search_string
@search_string.setter
def search_string(self, search_string):
"""Sets the search query made by a user.
Use only with Search events. A search query made by a user.
:param search_string: The search query.
:type: str
"""
self._search_string = search_string
@property
def delivery_category(self):
"""Gets the Type of Delivery Category.
:return: The Delivery Category type.
:rtype: DeliveryCategory
"""
return self._delivery_category
@delivery_category.setter
def delivery_category(self, delivery_category):
"""Sets the Type of Delivery Category.
Use with Purchase events.
:param delivery_category: The Delivery Category type.
:type: DeliveryCategory
"""
if not isinstance(delivery_category, DeliveryCategory):
raise TypeError('delivery_category must be of type DeliveryCategory. Passed invalid category: ' + delivery_category)
self._delivery_category = delivery_category
@property
def item_number(self):
"""Gets the item number.
:return: The item number.
:rtype: str
"""
return self._item_number
@item_number.setter
def item_number(self, item_number):
"""Sets the item number.
:param item_number: The item number.
:type: str
"""
self._item_number = item_number
@property
def custom_properties(self):
"""Gets the custom properties to be included in the Custom Data.
:return: The custom properties dictionary.
:rtype: dict
"""
return self._custom_properties
@custom_properties.setter
def custom_properties(self, custom_properties):
"""Sets the custom properties to be included in the Custom Data.
These are properties that are not defined in the standard list of the custom data.
:param custom_properties: The custom properties dictionary.
:type: dict
"""
self._custom_properties = custom_properties
def add_custom_property(self, key, value):
"""Adds the custom property (key, value) to the custom property bag.
:param key: The Key for the property to be added.
:type: str
:param value: The Value for the property to be added.
:type: str
"""
self.custom_properties[key] = value
def normalize(self):
normalized_payload = {
'value': self.value,
'currency': Normalize.normalize_field_skip_hashing('currency', self.currency),
'content_name': self.content_name,
'content_category': self.content_category,
'content_ids': self.content_ids,
'content_type': self.content_type,
'order_id': self.order_id,
'predicted_ltv': self.predicted_ltv,
'num_items': self.num_items,
'status': self.status,
'search_string': self.search_string,
'item_number': self.item_number,
}
if self.delivery_category is not None:
normalized_payload['delivery_category'] = self.delivery_category.value
if self.contents is not None:
contents = []
for content in self.contents:
if content is not None:
contents.append(content.normalize())
normalized_payload['contents'] = contents
# Append the custom_properties to the custom_data normalized payload.
if self.custom_properties:
for key in self.custom_properties:
if key in normalized_payload.keys():
raise Exception('Duplicate key in custom_properties:"' + key + '". Please make sure the keys defined in the custom_properties are not already available in standard custom_data property list.')
normalized_payload[key] = self.custom_properties[key]
normalized_payload = {k: v for k, v in normalized_payload.items() if v is not None}
return normalized_payload
def to_dict(self):
"""Returns the model properties as a dict"""
result = {}
for attr, _ in six.iteritems(self.param_types):
value = getattr(self, attr)
if isinstance(value, list):
result[attr] = list(map(
lambda x: x.to_dict() if hasattr(x, "to_dict") else x,
value
))
elif hasattr(value, "to_dict"):
result[attr] = value.to_dict()
elif isinstance(value, dict):
result[attr] = dict(map(
lambda item: (item[0], item[1].to_dict())
if hasattr(item[1], "to_dict") else item,
value.items()
))
else:
result[attr] = value
if issubclass(CustomData, dict):
for key, value in self.items():
result[key] = value
return result
def to_str(self):
"""Returns the string representation of the model"""
return pprint.pformat(self.to_dict())
def __repr__(self):
"""For `print` and `pprint`"""
return self.to_str()
def __eq__(self, other):
"""Returns true if both objects are equal"""
if not isinstance(other, CustomData):
return False
return self.__dict__ == other.__dict__
def __ne__(self, other):
"""Returns true if both objects are not equal"""
return not self == other