-
Notifications
You must be signed in to change notification settings - Fork 35
feat(eventprocessor): Datamodel for event processor #192
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
232f0a0
2320939
fbb5a68
8a5f531
c829fca
c3c9d46
23ab6ce
6474e98
d4f2c66
80b0963
071460a
900d96d
8fabcdb
a6ff8fe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# Copyright 2019, Optimizely | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
# Copyright 2019 Optimizely | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
|
||
class EventBatch(object): | ||
""" Class respresenting Event Batch. """ | ||
|
||
def __init__(self, account_id, project_id, revision, client_name, client_version, | ||
anonymize_ip, enrich_decisions, visitors=None): | ||
self.account_id = account_id | ||
self.project_id = project_id | ||
self.revision = revision | ||
self.client_name = client_name | ||
self.client_version = client_version | ||
self.anonymize_ip = anonymize_ip | ||
self.enrich_decisions = enrich_decisions | ||
self.visitors = visitors | ||
|
||
|
||
class Decision(object): | ||
""" Class respresenting Decision. """ | ||
|
||
def __init__(self, campaign_id, experiment_id, variation_id): | ||
self.campaign_id = campaign_id | ||
self.experiment_id = experiment_id | ||
self.variation_id = variation_id | ||
|
||
|
||
class Snapshot(object): | ||
""" Class representing Snapshot. """ | ||
|
||
def __init__(self, events, decisions=None): | ||
self.events = events | ||
self.decisions = decisions | ||
|
||
|
||
class SnapshotEvent(object): | ||
""" Class representing Snapshot Event. """ | ||
|
||
def __init__(self, entity_id, uuid, key, timestamp, revenue=None, value=None, tags=None): | ||
self.entity_id = entity_id | ||
self.uuid = uuid | ||
self.key = key | ||
self.timestamp = timestamp | ||
self.revenue = revenue | ||
self.value = value | ||
self.tags = tags | ||
|
||
|
||
class Visitor(object): | ||
""" Class representing Visitor. """ | ||
|
||
def __init__(self, snapshots, attributes, visitor_id): | ||
self.snapshots = snapshots | ||
self.attributes = attributes | ||
self.visitor_id = visitor_id | ||
|
||
|
||
class VisitorAttribute(object): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's just call this |
||
""" Class representing Visitor Attribute. """ | ||
|
||
def __init__(self, entity_id, key, event_type, value): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be |
||
self.entity_id = entity_id | ||
self.key = key | ||
self.type = event_type | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd refrain from using a variable called May be just say There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. attribute type is expected as 'type' keyword on server side and even if changed to 'attribute_type', it will have to be converted to 'type' eventually before sending to server. |
||
self.value = value |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
# Copyright 2019 Optimizely | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
import time | ||
import uuid | ||
|
||
from optimizely import version | ||
|
||
SDK_TYPE = 'python-sdk' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rename this to |
||
|
||
|
||
class UserEvent(object): | ||
""" Class respresenting User Event. """ | ||
|
||
def __init__(self, event_context): | ||
self.event_context = event_context | ||
self.uuid = self._get_uuid() | ||
self.timestamp = self._get_time() | ||
|
||
def _get_time(self): | ||
return int(round(time.time() * 1000)) | ||
|
||
def _get_uuid(self): | ||
return str(uuid.uuid4()) | ||
|
||
|
||
class ImpressionEvent(UserEvent): | ||
""" Class representing Impression Event. """ | ||
|
||
def __init__(self, event_context, user_id, experiment, visitor_attributes, variation, bot_filtering=None): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can probably move user_id, bot_filtering and attributes to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Feedback not addressed. Is there a reason for not moving this? |
||
super(ImpressionEvent, self).__init__(event_context) | ||
self.event_context = event_context | ||
self.user_id = user_id | ||
self.experiment = experiment | ||
self.visitor_attributes = visitor_attributes | ||
aliabbasrizvi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
self.variation = variation | ||
self.bot_filtering = bot_filtering | ||
|
||
|
||
class ConversionEvent(UserEvent): | ||
""" Class representing Conversion Event. """ | ||
|
||
def __init__(self, event_context, event, user_id, visitor_attributes, event_tags, bot_filtering=None): | ||
super(ConversionEvent, self).__init__(event_context) | ||
self.event_context = event_context | ||
self.event = event | ||
self.user_id = user_id | ||
self.visitor_attributes = visitor_attributes | ||
self.event_tags = event_tags | ||
self.bot_filtering = bot_filtering | ||
|
||
|
||
class EventContext(object): | ||
""" Class respresenting User Event Context. """ | ||
|
||
def __init__(self, account_id, project_id, revision, anonymize_ip): | ||
self.account_id = account_id | ||
self.project_id = project_id | ||
self.revision = revision | ||
self.client_name = SDK_TYPE | ||
self.client_version = version.__version__ | ||
self.anonymize_ip = anonymize_ip |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
# Copyright 2019, Optimizely | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
import json | ||
from operator import itemgetter | ||
|
||
from optimizely import version | ||
from optimizely.event.event_payload import Decision, EventBatch, Snapshot, SnapshotEvent, Visitor, VisitorAttribute | ||
from . import base | ||
|
||
|
||
class EventPayloadTest(base.BaseTest): | ||
aliabbasrizvi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
def _validate_event_object(self, expected_params, event_obj): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should ideally override |
||
""" Helper method to validate properties of the event object. """ | ||
|
||
expected_params['visitors'][0]['attributes'] = \ | ||
sorted(expected_params['visitors'][0]['attributes'], key=itemgetter('key')) | ||
event_obj['visitors'][0]['attributes'] = \ | ||
sorted(event_obj['visitors'][0]['attributes'], key=itemgetter('key')) | ||
self.assertEqual(expected_params, event_obj) | ||
|
||
def _dict_clean(self, obj): | ||
""" Helper method to remove keys from dictionary with None values. """ | ||
|
||
result = {} | ||
for k, v in obj: | ||
if v is None and k in ['revenue', 'value', 'tags', 'decisions']: | ||
continue | ||
else: | ||
result[k] = v | ||
return result | ||
|
||
def test_impression_event_equals_serialized_payload(self): | ||
expected_params = { | ||
'account_id': '12001', | ||
'project_id': '111001', | ||
'visitors': [{ | ||
'visitor_id': 'test_user', | ||
'attributes': [{ | ||
'type': 'custom', | ||
'value': 'test_value', | ||
'entity_id': '111094', | ||
'key': 'test_attribute' | ||
}], | ||
'snapshots': [{ | ||
'decisions': [{ | ||
'variation_id': '111129', | ||
'experiment_id': '111127', | ||
'campaign_id': '111182' | ||
}], | ||
'events': [{ | ||
'timestamp': 42123, | ||
'entity_id': '111182', | ||
'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', | ||
'key': 'campaign_activated' | ||
}] | ||
}] | ||
}], | ||
'client_name': 'python-sdk', | ||
'client_version': version.__version__, | ||
'enrich_decisions': True, | ||
'anonymize_ip': False, | ||
'revision': '42' | ||
} | ||
|
||
batch = EventBatch("12001", "111001", "42", "python-sdk", version.__version__, | ||
False, True) | ||
visitor_attr = VisitorAttribute("111094", "test_attribute", "custom", "test_value") | ||
event = SnapshotEvent("111182", "a68cf1ad-0393-4e18-af87-efe8f01a7c9c", "campaign_activated", | ||
42123) | ||
event_decision = Decision("111182", "111127", "111129") | ||
|
||
snapshots = Snapshot([event], [event_decision]) | ||
user = Visitor([snapshots], [visitor_attr], "test_user") | ||
|
||
batch.visitors = [user] | ||
|
||
self.maxDiff = None | ||
self._validate_event_object(expected_params, | ||
json.loads( | ||
json.dumps(batch.__dict__, default=lambda o: o.__dict__), | ||
object_pairs_hook=self._dict_clean | ||
)) | ||
|
||
def test_conversion_event_equals_serialized_payload(self): | ||
expected_params = { | ||
'account_id': '12001', | ||
'project_id': '111001', | ||
'visitors': [{ | ||
'visitor_id': 'test_user', | ||
'attributes': [{ | ||
'type': 'custom', | ||
'value': 'test_value', | ||
'entity_id': '111094', | ||
'key': 'test_attribute' | ||
}, { | ||
'type': 'custom', | ||
'value': 'test_value2', | ||
'entity_id': '111095', | ||
'key': 'test_attribute2' | ||
}], | ||
'snapshots': [{ | ||
'events': [{ | ||
'timestamp': 42123, | ||
'entity_id': '111182', | ||
'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', | ||
'key': 'campaign_activated', | ||
'revenue': 4200, | ||
'tags': { | ||
'non-revenue': 'abc', | ||
'revenue': 4200, | ||
'value': 1.234 | ||
}, | ||
'value': 1.234 | ||
}] | ||
}] | ||
}], | ||
'client_name': 'python-sdk', | ||
'client_version': version.__version__, | ||
'enrich_decisions': True, | ||
'anonymize_ip': False, | ||
'revision': '42' | ||
} | ||
|
||
batch = EventBatch("12001", "111001", "42", "python-sdk", version.__version__, | ||
False, True) | ||
visitor_attr_1 = VisitorAttribute("111094", "test_attribute", "custom", "test_value") | ||
visitor_attr_2 = VisitorAttribute("111095", "test_attribute2", "custom", "test_value2") | ||
event = SnapshotEvent("111182", "a68cf1ad-0393-4e18-af87-efe8f01a7c9c", "campaign_activated", | ||
42123, 4200, 1.234, {'revenue': 4200, 'value': 1.234, 'non-revenue': 'abc'}) | ||
|
||
snapshots = Snapshot([event]) | ||
user = Visitor([snapshots], [visitor_attr_1, visitor_attr_2], "test_user") | ||
|
||
batch.visitors = [user] | ||
|
||
self._validate_event_object(expected_params, | ||
json.loads( | ||
json.dumps(batch.__dict__, default=lambda o: o.__dict__), | ||
object_pairs_hook=self._dict_clean | ||
)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The word event in
event_payload
is redundant as it is already under event package. Perhaps just saypayload.py