Skip to content

Commit 6ba07f3

Browse files
Merge pull request #46 from optimizely/devel
Release 1.1.1
2 parents e4e27f4 + c0ccc97 commit 6ba07f3

18 files changed

+283
-1351
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# 1.1.1
2+
- Updated datafile parsing to be able to handle additional fields.
3+
- Deprecated Classic project support.
4+
15
# 1.1.0
26
- Included datafile revision information in log events.
37
- Added event tags to track API to allow users to pass in event metadata.

optimizely/entities.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2016, Optimizely
1+
# Copyright 2016-2017, Optimizely
22
# Licensed under the Apache License, Version 2.0 (the "License");
33
# you may not use this file except in compliance with the License.
44
# You may obtain a copy of the License at
@@ -19,15 +19,14 @@ def __eq__(self, other):
1919

2020
class Attribute(BaseEntity):
2121

22-
def __init__(self, id, key, segmentId=None):
22+
def __init__(self, id, key, **kwargs):
2323
self.id = id
2424
self.key = key
25-
self.segmentId = segmentId
2625

2726

2827
class Audience(BaseEntity):
2928

30-
def __init__(self, id, name, conditions, conditionStructure=None, conditionList=None):
29+
def __init__(self, id, name, conditions, conditionStructure=None, conditionList=None, **kwargs):
3130
self.id = id
3231
self.name = name
3332
self.conditions = conditions
@@ -37,7 +36,7 @@ def __init__(self, id, name, conditions, conditionStructure=None, conditionList=
3736

3837
class Event(BaseEntity):
3938

40-
def __init__(self, id, key, experimentIds):
39+
def __init__(self, id, key, experimentIds, **kwargs):
4140
self.id = id
4241
self.key = key
4342
self.experimentIds = experimentIds
@@ -46,7 +45,7 @@ def __init__(self, id, key, experimentIds):
4645
class Experiment(BaseEntity):
4746

4847
def __init__(self, id, key, status, audienceIds, variations, forcedVariations,
49-
trafficAllocation, layerId=None, groupId=None, groupPolicy=None, percentageIncluded=None):
48+
trafficAllocation, layerId, groupId=None, groupPolicy=None, **kwargs):
5049
self.id = id
5150
self.key = key
5251
self.status = status
@@ -57,12 +56,11 @@ def __init__(self, id, key, status, audienceIds, variations, forcedVariations,
5756
self.layerId = layerId
5857
self.groupId = groupId
5958
self.groupPolicy = groupPolicy
60-
self.percentageIncluded = percentageIncluded
6159

6260

6361
class Group(BaseEntity):
6462

65-
def __init__(self, id, policy, experiments, trafficAllocation):
63+
def __init__(self, id, policy, experiments, trafficAllocation, **kwargs):
6664
self.id = id
6765
self.policy = policy
6866
self.experiments = experiments
@@ -71,6 +69,6 @@ def __init__(self, id, policy, experiments, trafficAllocation):
7169

7270
class Variation(BaseEntity):
7371

74-
def __init__(self, id, key):
72+
def __init__(self, id, key, **kwargs):
7573
self.id = id
7674
self.key = key

optimizely/event_builder.py

Lines changed: 1 addition & 183 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,7 @@
1515
from abc import abstractmethod
1616
from abc import abstractproperty
1717

18-
from . import exceptions
19-
from . import project_config
2018
from . import version
21-
from .helpers import enums
2219
from .helpers import event_tag_utils
2320

2421

@@ -99,163 +96,7 @@ def _add_common_params(self, user_id, attributes):
9996
self._add_time()
10097

10198

102-
class EventBuilderV1(BaseEventBuilder):
103-
""" Class which encapsulates methods to build events for tracking
104-
impressions and conversions using the old endpoint. """
105-
106-
# Attribute mapping format
107-
ATTRIBUTE_PARAM_FORMAT = '{segment_prefix}{segment_id}'
108-
# Experiment mapping format
109-
EXPERIMENT_PARAM_FORMAT = '{experiment_prefix}{experiment_id}'
110-
# Event API format
111-
OFFLINE_API_PATH = 'https://{project_id}.log.optimizely.com/event'
112-
113-
114-
class EventParams(object):
115-
ACCOUNT_ID = 'd'
116-
PROJECT_ID = 'a'
117-
EXPERIMENT_PREFIX = 'x'
118-
GOAL_ID = 'g'
119-
GOAL_NAME = 'n'
120-
END_USER_ID = 'u'
121-
EVENT_VALUE = 'v'
122-
SEGMENT_PREFIX = 's'
123-
SOURCE = 'src'
124-
TIME = 'time'
125-
126-
def _add_attributes(self, attributes):
127-
""" Add attribute(s) information to the event.
128-
129-
Args:
130-
attributes: Dict representing user attributes and values which need to be recorded.
131-
"""
132-
133-
if not attributes:
134-
return
135-
136-
for attribute_key in attributes.keys():
137-
attribute_value = attributes.get(attribute_key)
138-
# Omit falsy attribute values
139-
if attribute_value:
140-
attribute = self.config.get_attribute(attribute_key)
141-
if attribute:
142-
self.params[self.ATTRIBUTE_PARAM_FORMAT.format(
143-
segment_prefix=self.EventParams.SEGMENT_PREFIX, segment_id=attribute.segmentId)] = attribute_value
144-
145-
def _add_source(self):
146-
""" Add source information to the event. """
147-
148-
self.params[self.EventParams.SOURCE] = 'python-sdk-{version}'.format(version=version.__version__)
149-
150-
def _add_time(self):
151-
""" Add time information to the event. """
152-
153-
self.params[self.EventParams.TIME] = int(time.time())
154-
155-
def _add_impression_goal(self, experiment):
156-
""" Add impression goal information to the event.
157-
158-
Args:
159-
experiment: Object representing experiment being activated.
160-
"""
161-
162-
# For tracking impressions, goal ID is set equal to experiment ID of experiment being activated
163-
self.params[self.EventParams.GOAL_ID] = experiment.id
164-
self.params[self.EventParams.GOAL_NAME] = 'visitor-event'
165-
166-
def _add_experiment(self, experiment, variation_id):
167-
""" Add experiment to variation mapping to the impression event.
168-
169-
Args:
170-
experiment: Object representing experiment being activated.
171-
variation_id: ID for variation which would be presented to user.
172-
"""
173-
174-
self.params[self.EXPERIMENT_PARAM_FORMAT.format(experiment_prefix=self.EventParams.EXPERIMENT_PREFIX,
175-
experiment_id=experiment.id)] = variation_id
176-
177-
def _add_experiment_variation_params(self, user_id, valid_experiments):
178-
""" Maps experiment and corresponding variation as parameters to be used in the event tracking call.
179-
180-
Args:
181-
user_id: ID for user.
182-
valid_experiments: List of tuples representing valid experiments for the event.
183-
"""
184-
185-
for experiment in valid_experiments:
186-
variation = self.bucketer.bucket(experiment, user_id)
187-
if variation:
188-
self.params[self.EXPERIMENT_PARAM_FORMAT.format(experiment_prefix=self.EventParams.EXPERIMENT_PREFIX,
189-
experiment_id=experiment.id)] = variation.id
190-
191-
def _add_conversion_goal(self, event_key, event_value):
192-
""" Add conversion goal information to the event.
193-
194-
Args:
195-
event_key: Event key representing the event which needs to be recorded.
196-
event_value: Value associated with the event. Can be used to represent revenue in cents.
197-
"""
198-
199-
event = self.config.get_event(event_key)
200-
201-
if not event:
202-
return
203-
204-
event_ids = event.id
205-
206-
if event_value:
207-
event_ids = '{goal_id},{revenue_goal_id}'.format(goal_id=event.id,
208-
revenue_goal_id=self.config.get_revenue_goal().id)
209-
self.params[self.EventParams.EVENT_VALUE] = event_value
210-
211-
self.params[self.EventParams.GOAL_ID] = event_ids
212-
self.params[self.EventParams.GOAL_NAME] = event_key
213-
214-
def create_impression_event(self, experiment, variation_id, user_id, attributes):
215-
""" Create impression Event to be sent to the logging endpoint.
216-
217-
Args:
218-
experiment: Object representing experiment for which impression needs to be recorded.
219-
variation_id: ID for variation which would be presented to user.
220-
user_id: ID for user.
221-
attributes: Dict representing user attributes and values which need to be recorded.
222-
223-
Returns:
224-
Event object encapsulating the impression event.
225-
"""
226-
227-
self.params = {}
228-
self._add_common_params(user_id, attributes)
229-
self._add_impression_goal(experiment)
230-
self._add_experiment(experiment, variation_id)
231-
return Event(self.OFFLINE_API_PATH.format(project_id=self.params[self.EventParams.PROJECT_ID]),
232-
self.params)
233-
234-
def create_conversion_event(self, event_key, user_id, attributes, event_tags, valid_experiments):
235-
""" Create conversion Event to be sent to the logging endpoint.
236-
237-
Args:
238-
event_key: Event key representing the event which needs to be recorded.
239-
user_id: ID for user.
240-
attributes: Dict representing user attributes and values.
241-
event_tags: Dict representing metadata associated with the event.
242-
valid_experiments: List of tuples representing valid experiments for the event.
243-
244-
Returns:
245-
Event object encapsulating the conversion event.
246-
"""
247-
248-
event_value = event_tag_utils.get_revenue_value(event_tags)
249-
250-
self.params = {}
251-
self._add_common_params(user_id, attributes)
252-
self._add_conversion_goal(event_key, event_value)
253-
self._add_experiment_variation_params(user_id, valid_experiments)
254-
return Event(self.OFFLINE_API_PATH.format(project_id=self.params[self.EventParams.PROJECT_ID]),
255-
self.params)
256-
257-
258-
class EventBuilderV2(BaseEventBuilder):
99+
class EventBuilder(BaseEventBuilder):
259100
""" Class which encapsulates methods to build events for tracking
260101
impressions and conversions using the new endpoints. """
261102

@@ -437,26 +278,3 @@ def create_conversion_event(self, event_key, user_id, attributes, event_tags, va
437278
self.params,
438279
http_verb=self.HTTP_VERB,
439280
headers=self.HTTP_HEADERS)
440-
441-
442-
def get_event_builder(config, bucketer):
443-
""" Helper method to get appropriate EventBuilder class based on the version of the datafile.
444-
445-
Args:
446-
config: Object representing the project's configuration.
447-
bucketer: Object representing the bucketer.
448-
449-
Returns:
450-
Event builder based on the version of the datafile.
451-
452-
Raises:
453-
Exception if provided datafile has unsupported version.
454-
"""
455-
456-
config_version = config.get_version()
457-
if config_version == project_config.V1_CONFIG_VERSION:
458-
return EventBuilderV1(config, bucketer)
459-
if config_version == project_config.V2_CONFIG_VERSION:
460-
return EventBuilderV2(config, bucketer)
461-
462-
raise exceptions.InvalidInputException(enums.Errors.UNSUPPORTED_DATAFILE_VERSION)

0 commit comments

Comments
 (0)