|
12 | 12 | # limitations under the License.
|
13 | 13 |
|
14 | 14 | import time
|
| 15 | +import uuid |
15 | 16 | from abc import abstractmethod
|
16 | 17 | from abc import abstractproperty
|
17 | 18 |
|
@@ -275,3 +276,208 @@ def create_conversion_event(self, event_key, user_id, attributes, event_tags, de
|
275 | 276 | self.params,
|
276 | 277 | http_verb=self.HTTP_VERB,
|
277 | 278 | headers=self.HTTP_HEADERS)
|
| 279 | + |
| 280 | + |
| 281 | +class EventBuilderV3(BaseEventBuilder): |
| 282 | + """ Class which encapsulates methods to build events for tracking |
| 283 | + impressions and conversions using the new V3 event API (batch). """ |
| 284 | + |
| 285 | + EVENTS_URL = 'https://logx.optimizely.com/v1/events' |
| 286 | + HTTP_VERB = 'POST' |
| 287 | + HTTP_HEADERS = {'Content-Type': 'application/json'} |
| 288 | + |
| 289 | + class EventParams(object): |
| 290 | + ACCOUNT_ID = 'account_id' |
| 291 | + PROJECT_ID = 'project_id' |
| 292 | + EXPERIMENT_ID = 'experiment_id' |
| 293 | + CAMPAIGN_ID = 'campaign_id' |
| 294 | + VARIATION_ID = 'variation_id' |
| 295 | + END_USER_ID = 'visitor_id' |
| 296 | + EVENTS = 'events' |
| 297 | + EVENT_ID = 'entity_id' |
| 298 | + ATTRIBUTES = 'attributes' |
| 299 | + DECISIONS = 'decisions' |
| 300 | + REVISION = 'revision' |
| 301 | + TIME = 'timestamp' |
| 302 | + KEY = 'key' |
| 303 | + TAGS = 'tags' |
| 304 | + UUID = 'uuid' |
| 305 | + USERS = 'visitors' |
| 306 | + SNAPSHOTS = 'snapshots' |
| 307 | + SOURCE_SDK_TYPE = 'client_name' |
| 308 | + SOURCE_SDK_VERSION = 'client_version' |
| 309 | + CUSTOM = 'custom' |
| 310 | + |
| 311 | + def _add_attributes(self, attributes): |
| 312 | + """ Add attribute(s) information to the event. |
| 313 | +
|
| 314 | + Args: |
| 315 | + attributes: Dict representing user attributes and values which need to be recorded. |
| 316 | + """ |
| 317 | + |
| 318 | + if not attributes: |
| 319 | + return |
| 320 | + |
| 321 | + visitor = self.params[self.EventParams.USERS][0] |
| 322 | + visitor[self.EventParams.ATTRIBUTES] = [] |
| 323 | + |
| 324 | + for attribute_key in attributes.keys(): |
| 325 | + attribute_value = attributes.get(attribute_key) |
| 326 | + # Omit falsy attribute values |
| 327 | + if attribute_value: |
| 328 | + attribute = self.config.get_attribute(attribute_key) |
| 329 | + if attribute: |
| 330 | + visitor[self.EventParams.ATTRIBUTES].append({ |
| 331 | + self.EventParams.EVENT_ID: attribute.id, |
| 332 | + 'key': attribute_key, |
| 333 | + 'type': self.EventParams.CUSTOM, |
| 334 | + 'value': attribute_value, |
| 335 | + }) |
| 336 | + |
| 337 | + def _add_source(self): |
| 338 | + """ Add source information to the event. """ |
| 339 | + |
| 340 | + self.params[self.EventParams.SOURCE_SDK_TYPE] = 'python-sdk' |
| 341 | + self.params[self.EventParams.SOURCE_SDK_VERSION] = version.__version__ |
| 342 | + |
| 343 | + def _add_revision(self): |
| 344 | + """ Add datafile revision information to the event. """ |
| 345 | + self.params[self.EventParams.REVISION] = self.config.get_revision() |
| 346 | + |
| 347 | + def _add_time(self): |
| 348 | + """ Add time information to the event. """ |
| 349 | + |
| 350 | + self.params[self.EventParams.TIME] = int(round(time.time() * 1000)) |
| 351 | + |
| 352 | + def _add_visitor(self, user_id): |
| 353 | + """ Add user to the event """ |
| 354 | + |
| 355 | + self.params[self.EventParams.USERS] = [] |
| 356 | + # Add a single visitor |
| 357 | + visitor = {} |
| 358 | + visitor[self.EventParams.END_USER_ID] = user_id |
| 359 | + visitor[self.EventParams.SNAPSHOTS] = [] |
| 360 | + self.params[self.EventParams.USERS].append(visitor) |
| 361 | + |
| 362 | + def _add_common_params(self, user_id, attributes): |
| 363 | + """ Add params which are used same in both conversion and impression events. |
| 364 | +
|
| 365 | + Args: |
| 366 | + user_id: ID for user. |
| 367 | + attributes: Dict representing user attributes and values which need to be recorded. |
| 368 | + """ |
| 369 | + self._add_project_id() |
| 370 | + self._add_account_id() |
| 371 | + self._add_visitor(user_id) |
| 372 | + self._add_attributes(attributes) |
| 373 | + self._add_source() |
| 374 | + self._add_revision() |
| 375 | + |
| 376 | + def _add_required_params_for_impression(self, experiment, variation_id): |
| 377 | + """ Add parameters that are required for the impression event to register. |
| 378 | +
|
| 379 | + Args: |
| 380 | + experiment: Experiment for which impression needs to be recorded. |
| 381 | + variation_id: ID for variation which would be presented to user. |
| 382 | + """ |
| 383 | + snapshot = {} |
| 384 | + |
| 385 | + snapshot[self.EventParams.DECISIONS] = [{ |
| 386 | + self.EventParams.EXPERIMENT_ID: experiment.id, |
| 387 | + self.EventParams.VARIATION_ID: variation_id, |
| 388 | + self.EventParams.CAMPAIGN_ID: experiment.layerId |
| 389 | + }] |
| 390 | + |
| 391 | + snapshot[self.EventParams.EVENTS] = [{ |
| 392 | + self.EventParams.EVENT_ID: experiment.layerId, |
| 393 | + self.EventParams.TIME: int(round(time.time() * 1000)), |
| 394 | + self.EventParams.KEY: 'campaign_activated', |
| 395 | + self.EventParams.UUID: str(uuid.uuid4()) |
| 396 | + }] |
| 397 | + |
| 398 | + visitor = self.params[self.EventParams.USERS][0] |
| 399 | + visitor[self.EventParams.SNAPSHOTS].append(snapshot) |
| 400 | + |
| 401 | + def _add_required_params_for_conversion(self, event_key, event_tags, decisions): |
| 402 | + """ Add parameters that are required for the conversion event to register. |
| 403 | +
|
| 404 | + Args: |
| 405 | + event_key: Key representing the event which needs to be recorded. |
| 406 | + event_tags: Dict representing metadata associated with the event. |
| 407 | + decisions: List of tuples representing valid experiments IDs and variation IDs. |
| 408 | + """ |
| 409 | + |
| 410 | + visitor = self.params[self.EventParams.USERS][0] |
| 411 | + |
| 412 | + for experiment_id, variation_id in decisions: |
| 413 | + snapshot = {} |
| 414 | + experiment = self.config.get_experiment_from_id(experiment_id) |
| 415 | + |
| 416 | + if variation_id: |
| 417 | + snapshot[self.EventParams.DECISIONS] = [{ |
| 418 | + self.EventParams.EXPERIMENT_ID: experiment_id, |
| 419 | + self.EventParams.VARIATION_ID: variation_id, |
| 420 | + self.EventParams.CAMPAIGN_ID: experiment.layerId |
| 421 | + }] |
| 422 | + |
| 423 | + event_dict = { |
| 424 | + self.EventParams.EVENT_ID: self.config.get_event(event_key).id, |
| 425 | + self.EventParams.TIME: int(round(time.time() * 1000)), |
| 426 | + self.EventParams.KEY: event_key, |
| 427 | + self.EventParams.UUID: str(uuid.uuid4()) |
| 428 | + } |
| 429 | + |
| 430 | + if event_tags: |
| 431 | + event_value = event_tag_utils.get_revenue_value(event_tags) |
| 432 | + if event_value is not None: |
| 433 | + event_dict['revenue'] = event_value |
| 434 | + |
| 435 | + if len(event_tags) > 0: |
| 436 | + event_dict[self.EventParams.TAGS] = event_tags |
| 437 | + |
| 438 | + snapshot[self.EventParams.EVENTS] = [event_dict] |
| 439 | + visitor[self.EventParams.SNAPSHOTS].append(snapshot) |
| 440 | + |
| 441 | + def create_impression_event(self, experiment, variation_id, user_id, attributes): |
| 442 | + """ Create impression Event to be sent to the logging endpoint. |
| 443 | +
|
| 444 | + Args: |
| 445 | + experiment: Experiment for which impression needs to be recorded. |
| 446 | + variation_id: ID for variation which would be presented to user. |
| 447 | + user_id: ID for user. |
| 448 | + attributes: Dict representing user attributes and values which need to be recorded. |
| 449 | +
|
| 450 | + Returns: |
| 451 | + Event object encapsulating the impression event. |
| 452 | + """ |
| 453 | + |
| 454 | + self.params = {} |
| 455 | + self._add_common_params(user_id, attributes) |
| 456 | + self._add_required_params_for_impression(experiment, variation_id) |
| 457 | + |
| 458 | + return Event(self.EVENTS_URL, |
| 459 | + self.params, |
| 460 | + http_verb=self.HTTP_VERB, |
| 461 | + headers=self.HTTP_HEADERS) |
| 462 | + |
| 463 | + def create_conversion_event(self, event_key, user_id, attributes, event_tags, decisions): |
| 464 | + """ Create conversion Event to be sent to the logging endpoint. |
| 465 | +
|
| 466 | + Args: |
| 467 | + event_key: Key representing the event which needs to be recorded. |
| 468 | + user_id: ID for user. |
| 469 | + attributes: Dict representing user attributes and values. |
| 470 | + event_tags: Dict representing metadata associated with the event. |
| 471 | + decisions: List of tuples representing experiments IDs and variation IDs. |
| 472 | +
|
| 473 | + Returns: |
| 474 | + Event object encapsulating the conversion event. |
| 475 | + """ |
| 476 | + |
| 477 | + self.params = {} |
| 478 | + self._add_common_params(user_id, attributes) |
| 479 | + self._add_required_params_for_conversion(event_key, event_tags, decisions) |
| 480 | + return Event(self.EVENTS_URL, |
| 481 | + self.params, |
| 482 | + http_verb=self.HTTP_VERB, |
| 483 | + headers=self.HTTP_HEADERS) |
0 commit comments