Skip to content

Commit 3b99056

Browse files
Deprecating v1 event builder (#42)
1 parent e4e27f4 commit 3b99056

File tree

10 files changed

+68
-929
lines changed

10 files changed

+68
-929
lines changed

optimizely/event_builder.py

Lines changed: 2 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -99,163 +99,7 @@ def _add_common_params(self, user_id, attributes):
9999
self._add_time()
100100

101101

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):
102+
class EventBuilder(BaseEventBuilder):
259103
""" Class which encapsulates methods to build events for tracking
260104
impressions and conversions using the new endpoints. """
261105

@@ -454,9 +298,7 @@ def get_event_builder(config, bucketer):
454298
"""
455299

456300
config_version = config.get_version()
457-
if config_version == project_config.V1_CONFIG_VERSION:
458-
return EventBuilderV1(config, bucketer)
459301
if config_version == project_config.V2_CONFIG_VERSION:
460-
return EventBuilderV2(config, bucketer)
302+
return EventBuilder(config, bucketer)
461303

462304
raise exceptions.InvalidInputException(enums.Errors.UNSUPPORTED_DATAFILE_VERSION)

tests/base.py

Lines changed: 2 additions & 118 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
@@ -17,123 +17,7 @@
1717
from optimizely import optimizely
1818

1919

20-
class BaseTestV1(unittest.TestCase):
21-
22-
def setUp(self):
23-
self.config_dict = {
24-
'revision': '42',
25-
'version': '1',
26-
'events': [{
27-
'key': 'test_event',
28-
'experimentIds': ['111127'],
29-
'id': '111095'
30-
}, {
31-
'key': 'Total Revenue',
32-
'experimentIds': ['111127'],
33-
'id': '111096'
34-
}],
35-
'experiments': [{
36-
'key': 'test_experiment',
37-
'status': 'Running',
38-
'forcedVariations': {
39-
'user_1': 'control',
40-
'user_2': 'control'
41-
},
42-
'audienceIds': ['11154'],
43-
'trafficAllocation': [{
44-
'entityId': '111128',
45-
'endOfRange': 4000
46-
}, {
47-
'entityId': '111129',
48-
'endOfRange': 9000
49-
}],
50-
'id': '111127',
51-
'variations': [{
52-
'key': 'control',
53-
'id': '111128'
54-
}, {
55-
'key': 'variation',
56-
'id': '111129'
57-
}]
58-
}],
59-
'groups': [{
60-
'id': '19228',
61-
'policy': 'random',
62-
'experiments': [{
63-
'id': '32222',
64-
'key': 'group_exp_1',
65-
'status': 'Running',
66-
'audienceIds': [],
67-
'variations': [{
68-
'key': 'group_exp_1_control',
69-
'id': '28901'
70-
}, {
71-
'key': 'group_exp_1_variation',
72-
'id': '28902'
73-
}],
74-
'forcedVariations': {
75-
'user_1': 'group_exp_1_control',
76-
'user_2': 'group_exp_1_control'
77-
},
78-
'trafficAllocation': [{
79-
'entityId': '28901',
80-
'endOfRange': 3000
81-
}, {
82-
'entityId': '28902',
83-
'endOfRange': 9000
84-
}]
85-
}, {
86-
'id': '32223',
87-
'key': 'group_exp_2',
88-
'status': 'Running',
89-
'audienceIds': [],
90-
'variations': [{
91-
'key': 'group_exp_2_control',
92-
'id': '28905'
93-
}, {
94-
'key': 'group_exp_2_variation',
95-
'id': '28906'
96-
}],
97-
'forcedVariations': {
98-
'user_1': 'group_exp_2_control',
99-
'user_2': 'group_exp_2_control'
100-
},
101-
'trafficAllocation': [{
102-
'entityId': '28905',
103-
'endOfRange': 8000
104-
}, {
105-
'entityId': '28906',
106-
'endOfRange': 10000
107-
}]
108-
}],
109-
'trafficAllocation': [{
110-
'entityId': '32222',
111-
"endOfRange": 3000
112-
}, {
113-
'entityId': '32223',
114-
'endOfRange': 7500
115-
}]
116-
}],
117-
'accountId': '12001',
118-
'dimensions': [{
119-
'key': 'test_attribute',
120-
'id': '111094',
121-
'segmentId': '11133'
122-
}],
123-
'audiences': [{
124-
'name': 'Test attribute users',
125-
'conditions': '["and", ["or", ["or", '
126-
'{"name": "test_attribute", "type": "custom_dimension", "value": "test_value"}]]]',
127-
'id': '11154'
128-
}],
129-
'projectId': '111001'
130-
}
131-
132-
self.optimizely = optimizely.Optimizely(json.dumps(self.config_dict))
133-
self.project_config = self.optimizely.config
134-
135-
136-
class BaseTestV2(unittest.TestCase):
20+
class BaseTest(unittest.TestCase):
13721

13822
def setUp(self):
13923
self.config_dict = {

tests/helpers_tests/test_audience.py

Lines changed: 2 additions & 2 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
@@ -17,7 +17,7 @@
1717
from optimizely.helpers import audience
1818

1919

20-
class AudienceTest(base.BaseTestV1):
20+
class AudienceTest(base.BaseTest):
2121

2222
def test_is_match__audience_condition_matches(self):
2323
""" Test that is_match returns True when audience conditions are met. """

tests/helpers_tests/test_condition.py

Lines changed: 4 additions & 4 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
@@ -18,10 +18,10 @@
1818
from tests import base
1919

2020

21-
class ConditionEvaluatorTests(base.BaseTestV1):
21+
class ConditionEvaluatorTests(base.BaseTest):
2222

2323
def setUp(self):
24-
base.BaseTestV1.setUp(self)
24+
base.BaseTest.setUp(self)
2525
self.condition_structure, self.condition_list = condition_helper.loads(
2626
self.config_dict['audiences'][0]['conditions']
2727
)
@@ -111,7 +111,7 @@ def test_evaluate__returns_false(self):
111111
self.assertFalse(self.condition_evaluator.evaluate(condition_structure))
112112

113113

114-
class ConditionDecoderTests(base.BaseTestV1):
114+
class ConditionDecoderTests(base.BaseTest):
115115

116116
def test_loads(self):
117117
""" Test that loads correctly sets condition structure and list. """

tests/helpers_tests/test_experiment.py

Lines changed: 2 additions & 2 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
@@ -18,7 +18,7 @@
1818
from optimizely.helpers import experiment
1919

2020

21-
class ExperimentTest(base.BaseTestV1):
21+
class ExperimentTest(base.BaseTest):
2222

2323
def test_is_experiment_running__status_running(self):
2424
""" Test that is_experiment_running returns True when experiment has Running status. """

tests/helpers_tests/test_validator.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from tests import base
2222

2323

24-
class ValidatorTest(base.BaseTestV1):
24+
class ValidatorTest(base.BaseTest):
2525

2626
def test_is_datafile_valid__returns_true(self):
2727
""" Test that valid datafile returns True. """
@@ -102,7 +102,7 @@ def test_are_event_tags_valid__returns_false(self):
102102
self.assertFalse(validator.are_event_tags_valid(42))
103103

104104

105-
class DatafileV2ValidationTests(base.BaseTestV2):
105+
class DatafileValidationTests(base.BaseTest):
106106

107107
def test_is_datafile_valid__returns_true(self):
108108
""" Test that valid datafile returns True. """

0 commit comments

Comments
 (0)