kameleoon-client-python 3.13.0__tar.gz → 3.15.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. {kameleoon-client-python-3.13.0/kameleoon_client_python.egg-info → kameleoon-client-python-3.15.0}/PKG-INFO +1 -1
  2. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/configuration/custom_data_info.py +7 -0
  3. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/configuration/data_file.py +38 -17
  4. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/configuration/feature_flag.py +8 -8
  5. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/configuration/rule.py +7 -10
  6. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/data.py +1 -0
  7. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/manager/assigned_variation.py +1 -1
  8. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/manager/visitor.py +20 -0
  9. kameleoon-client-python-3.15.0/kameleoon/data/targeted_segment.py +34 -0
  10. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/kameleoon_client.py +53 -31
  11. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/query_builder.py +1 -0
  12. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/services/data_service_impl.py +1 -1
  13. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/url_provider.py +1 -1
  14. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/sdk_version.py +1 -1
  15. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/exclusive_experiment_condition.py +3 -1
  16. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/segment_condition.py +2 -2
  17. kameleoon-client-python-3.15.0/kameleoon/targeting/models.py +28 -0
  18. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/targeting_manager.py +16 -14
  19. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0/kameleoon_client_python.egg-info}/PKG-INFO +1 -1
  20. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon_client_python.egg-info/SOURCES.txt +1 -0
  21. kameleoon-client-python-3.13.0/kameleoon/targeting/models.py +0 -29
  22. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/MANIFEST.in +0 -0
  23. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/README.md +0 -0
  24. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/__init__.py +0 -0
  25. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/client_readiness/__init__.py +0 -0
  26. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/client_readiness/async_readiness.py +0 -0
  27. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/client_readiness/threading_readiness.py +0 -0
  28. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/configuration/__init__.py +0 -0
  29. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/configuration/experiment.py +0 -0
  30. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/configuration/me_group.py +0 -0
  31. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/configuration/rule_type.py +0 -0
  32. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/configuration/settings.py +0 -0
  33. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/configuration/variable.py +0 -0
  34. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/configuration/variation.py +0 -0
  35. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/configuration/variation_by_exposition.py +0 -0
  36. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/__init__.py +0 -0
  37. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/browser.py +0 -0
  38. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/cbscores.py +0 -0
  39. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/conversion.py +0 -0
  40. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/cookie.py +0 -0
  41. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/custom_data.py +0 -0
  42. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/device.py +0 -0
  43. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/geolocation.py +0 -0
  44. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/kcs_heat.py +0 -0
  45. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/manager/__init__.py +0 -0
  46. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/manager/forced_experiment_variation.py +0 -0
  47. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/manager/forced_feature_variation.py +0 -0
  48. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/manager/forced_variation.py +0 -0
  49. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/manager/page_view_visit.py +0 -0
  50. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/manager/visitor_manager.py +0 -0
  51. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/manager/visitor_slot.py +0 -0
  52. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/mapping_identifier.py +0 -0
  53. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/operating_system.py +0 -0
  54. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/page_view.py +0 -0
  55. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/personalization.py +0 -0
  56. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/unique_identifier.py +0 -0
  57. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/user_agent.py +0 -0
  58. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/visitor_visits.py +0 -0
  59. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/defaults.py +0 -0
  60. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/exceptions.py +0 -0
  61. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/helpers/__init__.py +0 -0
  62. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/helpers/domain.py +0 -0
  63. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/helpers/functions.py +0 -0
  64. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/helpers/hasher.py +0 -0
  65. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/helpers/logger.py +0 -0
  66. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/helpers/multi_threading.py +0 -0
  67. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/helpers/nonce.py +0 -0
  68. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/helpers/repeat_timer.py +0 -0
  69. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/helpers/scheduler.py +0 -0
  70. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/helpers/string_utils.py +0 -0
  71. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/helpers/visitor_code.py +0 -0
  72. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/hybrid/__init__.py +0 -0
  73. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/hybrid/hybrid_manager.py +0 -0
  74. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/hybrid/hybrid_manager_impl.py +0 -0
  75. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/kameleoon_client_config.py +0 -0
  76. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/kameleoon_client_factory.py +0 -0
  77. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/logging/__init__.py +0 -0
  78. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/logging/kameleoon_logger.py +0 -0
  79. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/logging/log_level.py +0 -0
  80. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/logging/logger.py +0 -0
  81. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/__init__.py +0 -0
  82. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/asyncexec/__init__.py +0 -0
  83. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/asyncexec/async_executor.py +0 -0
  84. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/data/__init__.py +0 -0
  85. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/data/data_manager.py +0 -0
  86. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/evaluation/__init__.py +0 -0
  87. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/evaluation/evaluated_experiment.py +0 -0
  88. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/remote_data/__init__.py +0 -0
  89. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/remote_data/remote_data_manager.py +0 -0
  90. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/remote_data/remote_visitor_data.py +0 -0
  91. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/tracking/__init__.py +0 -0
  92. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/tracking/tracking_builder.py +0 -0
  93. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/tracking/tracking_manager.py +0 -0
  94. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/tracking/visitor_tracking_registry.py +0 -0
  95. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/warehouse/__init__.py +0 -0
  96. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/warehouse/warehouse_manager.py +0 -0
  97. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/middleware.py +0 -0
  98. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/__init__.py +0 -0
  99. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/access_token_source.py +0 -0
  100. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/access_token_source_factory.py +0 -0
  101. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/activity_event.py +0 -0
  102. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/cookie/__init__.py +0 -0
  103. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/cookie/cookie_manager.py +0 -0
  104. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/net_provider.py +0 -0
  105. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/net_provider_impl.py +0 -0
  106. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/network_manager.py +0 -0
  107. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/network_manager_factory.py +0 -0
  108. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/network_manager_factory_impl.py +0 -0
  109. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/network_manager_impl.py +0 -0
  110. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/query_encodable.py +0 -0
  111. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/sendable.py +0 -0
  112. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/service_initialize_context.py +0 -0
  113. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/service_initialize_context_impl.py +0 -0
  114. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/services/__init__.py +0 -0
  115. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/services/automation_service.py +0 -0
  116. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/services/automation_service_impl.py +0 -0
  117. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/services/configuration_service.py +0 -0
  118. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/services/configuration_service_impl.py +0 -0
  119. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/services/data_service.py +0 -0
  120. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/services/service.py +0 -0
  121. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/services/service_impl.py +0 -0
  122. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/uri_helper.py +0 -0
  123. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/real_time/__init__.py +0 -0
  124. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/real_time/real_time_configuration_service.py +0 -0
  125. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/real_time/real_time_event.py +0 -0
  126. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/storage/__init__.py +0 -0
  127. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/storage/cache.py +0 -0
  128. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/storage/cache_factory.py +0 -0
  129. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/storage/cache_factory_impl.py +0 -0
  130. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/storage/cache_impl.py +0 -0
  131. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/__init__.py +0 -0
  132. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/__init__.py +0 -0
  133. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/browser_condition.py +0 -0
  134. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/constants.py +0 -0
  135. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/conversion_condition.py +0 -0
  136. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/cookie_condition.py +0 -0
  137. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/custom_datum.py +0 -0
  138. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/device_condition.py +0 -0
  139. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/geolocation_condition.py +0 -0
  140. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/kcs_heat_range_condition.py +0 -0
  141. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/number_condition.py +0 -0
  142. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/operating_system_condition.py +0 -0
  143. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/page_title_condition.py +0 -0
  144. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/page_url_condition.py +0 -0
  145. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/page_view_number_condition.py +0 -0
  146. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/previous_page_condition.py +0 -0
  147. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/sdk_language_condition.py +0 -0
  148. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/string_value_condition.py +0 -0
  149. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/target_experiment_condition.py +0 -0
  150. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/target_feature_flag_condition.py +0 -0
  151. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/target_personalization_condition.py +0 -0
  152. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/targeting_condition.py +0 -0
  153. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/time_elapsed_since_visit_condition.py +0 -0
  154. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/unknown_condition.py +0 -0
  155. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/visit_number_today_condition.py +0 -0
  156. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/visit_number_total_condition.py +0 -0
  157. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/visitor_code_condition.py +0 -0
  158. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/visitor_new_return_condition.py +0 -0
  159. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/tree_builder.py +0 -0
  160. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/tree_condition_factory.py +0 -0
  161. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/types/__init__.py +0 -0
  162. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/types/remote_visitor_data_filter.py +0 -0
  163. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/types/variable.py +0 -0
  164. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/types/variation.py +0 -0
  165. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon_client_python.egg-info/dependency_links.txt +0 -0
  166. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon_client_python.egg-info/requires.txt +14 -14
  167. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon_client_python.egg-info/top_level.txt +0 -0
  168. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/setup.cfg +0 -0
  169. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/setup.py +0 -0
  170. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/tests/integration/proj/__init__.py +0 -0
  171. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/tests/integration/proj/kameleoon_app/__init__.py +0 -0
  172. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/tests/integration/proj/kameleoon_app/admin.py +0 -0
  173. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/tests/integration/proj/kameleoon_app/apps.py +0 -0
  174. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/tests/integration/proj/kameleoon_app/migrations/__init__.py +0 -0
  175. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/tests/integration/proj/kameleoon_app/models.py +0 -0
  176. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/tests/integration/proj/kameleoon_app/tests.py +0 -0
  177. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/tests/integration/proj/kameleoon_app/views.py +0 -0
  178. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/tests/integration/proj/manage.py +0 -0
  179. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/tests/integration/proj/proj/__init__.py +0 -0
  180. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/tests/integration/proj/proj/asgi.py +0 -0
  181. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/tests/integration/proj/proj/settings.py +0 -0
  182. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/tests/integration/proj/proj/urls.py +0 -0
  183. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/tests/integration/proj/proj/wsgi.py +0 -0
  184. {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/tests/resources/config.yml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kameleoon-client-python
3
- Version: 3.13.0
3
+ Version: 3.15.0
4
4
  Summary: Kameleoon Client Python Software Development Kit.
5
5
  Home-page: https://developers.kameleoon.com/python-sdk.html
6
6
  Author: Kameleoon
@@ -17,6 +17,7 @@ class CustomDataInfo:
17
17
  self.__mapping_identifier_index = None
18
18
  self.__local_only = set()
19
19
  self.__visitor_scope = set()
20
+ self.__custom_data_index_by_id = {}
20
21
  if custom_data is not None:
21
22
  for data in custom_data:
22
23
  index = data.get("index")
@@ -26,6 +27,8 @@ class CustomDataInfo:
26
27
  self.__local_only.add(index)
27
28
  if data.get("scope") == self.SCOPE_VISITOR:
28
29
  self.__visitor_scope.add(index)
30
+ if (id_ := data.get("id")) is not None:
31
+ self.__custom_data_index_by_id[id_] = index
29
32
  if data.get("isMappingIdentifier"):
30
33
  if bool(self.__mapping_identifier_index is not None):
31
34
  KameleoonLogger.warning(
@@ -50,3 +53,7 @@ class CustomDataInfo:
50
53
  def mapping_identifier_index(self) -> Optional[int]:
51
54
  """Take mapping identifier index"""
52
55
  return self.__mapping_identifier_index
56
+
57
+ def get_custom_data_index_by_id(self, custom_data_id: int) -> Optional[int]:
58
+ """Get corresponding custom data id by custom data index"""
59
+ return self.__custom_data_index_by_id.get(custom_data_id)
@@ -11,6 +11,7 @@ from kameleoon.configuration.settings import Settings
11
11
  from kameleoon.configuration.variation_by_exposition import VariationByExposition
12
12
  from kameleoon.exceptions import FeatureNotFound, FeatureEnvironmentDisabled
13
13
  from kameleoon.logging.kameleoon_logger import KameleoonLogger
14
+ from kameleoon.targeting.models import Segment
14
15
 
15
16
 
16
17
  class DataFile:
@@ -19,7 +20,7 @@ class DataFile:
19
20
  @staticmethod
20
21
  def default(environment: Optional[str]) -> "DataFile":
21
22
  """Creates new instance of `DataFile` initialized with default values"""
22
- data_file = DataFile(environment, None, Settings(), {}, CustomDataInfo(None))
23
+ data_file = DataFile(environment, None, Settings(), {}, {}, CustomDataInfo(None))
23
24
  return data_file
24
25
 
25
26
  @staticmethod
@@ -28,33 +29,50 @@ class DataFile:
28
29
  ) -> "DataFile":
29
30
  """Creates new instance of `DataFile` initialized from the specified configuration JSON"""
30
31
  settings = Settings(configuration.get("configuration"))
31
- feature_flags = {
32
- (feature_flag := FeatureFlag(jobj)).feature_key: feature_flag
33
- for jobj in configuration.get("featureFlags") or []
34
- }
32
+ segments = DataFile.__parse_segments(configuration)
35
33
  custom_data_info = CustomDataInfo(configuration.get("customData"))
34
+ feature_flags = DataFile.__parse_feature_flags(configuration, segments, custom_data_info)
36
35
  holdout = Experiment.from_json(configuration["holdout"]) if "holdout" in configuration else None
37
- data_file = DataFile(environment, last_modified, settings, feature_flags, custom_data_info, holdout)
36
+ data_file = DataFile(environment, last_modified, settings, segments, feature_flags, custom_data_info, holdout)
38
37
  return data_file
39
38
 
39
+ @staticmethod
40
+ def __parse_segments(configuration: Dict[str, Any]) -> Dict[int, Segment]:
41
+ return {
42
+ (seg := Segment.from_json(jobj)).id_: seg
43
+ for jobj in configuration.get("segments") or []
44
+ }
45
+
46
+ @staticmethod
47
+ def __parse_feature_flags(
48
+ configuration: Dict[str, Any], segments: Dict[int, Segment], cdi: CustomDataInfo
49
+ ) -> Dict[str, FeatureFlag]:
50
+ return {
51
+ (ff := FeatureFlag(jobj, segments, cdi)).feature_key: ff # pylint: disable=C0103
52
+ for jobj in configuration.get("featureFlags") or []
53
+ }
54
+
40
55
  # pylint: disable=R0913
41
56
  def __init__(
42
57
  self,
43
58
  environment: Optional[str],
44
59
  last_modified: Optional[str],
45
60
  settings: Settings,
61
+ segments: Dict[int, Segment],
46
62
  feature_flags: Dict[str, FeatureFlag],
47
63
  custom_data_info: CustomDataInfo,
48
64
  holdout: Optional[Experiment] = None,
49
65
  ) -> None:
50
66
  KameleoonLogger.debug(
51
- "CALL: DataFile(environment: %s, last_modified: %s, settings: %s, feature_flags: %s,"
67
+ "CALL: DataFile(environment: %s, last_modified: %s, settings: %s, segments, feature_flags,"
52
68
  " custom_data_info: %s, holdout: %s)",
53
- environment, last_modified, settings, feature_flags, custom_data_info, holdout
69
+ environment, last_modified, settings, custom_data_info, holdout
54
70
  )
55
71
  self.__environment = environment # pylint: disable=W0238
56
72
  self.__last_modified = last_modified
57
73
  self.__settings = settings
74
+ self.__segments = segments
75
+ self.__audience_tracking_segments = [seg for seg in segments.values() if seg.audience_tracking]
58
76
  self.__feature_flags: Dict[str, FeatureFlag] = feature_flags
59
77
  self.__me_groups = self.__make_me_groups(feature_flags.values())
60
78
  self.__has_any_targeted_delivery_rule = any(
@@ -64,7 +82,6 @@ class DataFile:
64
82
  for rule in ff.rules
65
83
  )
66
84
  self.__variation_by_id: Dict[int, VariationByExposition] = {}
67
- self.__rule_by_segment_id: Dict[int, Rule] = {}
68
85
  self.__rule_info_by_exp_id: Dict[int, DataFile.RuleInfo] = {}
69
86
  self.__feature_flag_by_id: Dict[int, FeatureFlag] = {}
70
87
  self.__experiment_ids_with_js_css_variable: Set[int] = set()
@@ -72,9 +89,9 @@ class DataFile:
72
89
  self.__custom_data_info = custom_data_info
73
90
  self.__holdout = holdout
74
91
  KameleoonLogger.debug(
75
- "RETURN: DataFile(environment: %s, last_modified: %s, settings: %s, feature_flags: %s,"
92
+ "RETURN: DataFile(environment: %s, last_modified: %s, settings: %s, segments, feature_flags,"
76
93
  " custom_data_info: %s, holdout: %s)",
77
- environment, last_modified, settings, feature_flags, custom_data_info, holdout
94
+ environment, last_modified, settings, custom_data_info, holdout
78
95
  )
79
96
 
80
97
  @property
@@ -87,6 +104,16 @@ class DataFile:
87
104
  """Returns settings"""
88
105
  return self.__settings
89
106
 
107
+ @property
108
+ def segments(self) -> Dict[int, Segment]:
109
+ """Returns segments"""
110
+ return self.__segments
111
+
112
+ @property
113
+ def audience_tracking_segments(self) -> List[Segment]:
114
+ """Returns audience tracking segments"""
115
+ return self.__audience_tracking_segments
116
+
90
117
  @property
91
118
  def feature_flags(self) -> Dict[str, FeatureFlag]:
92
119
  """Returns dictionary of all feature flags stored by feature keys"""
@@ -102,11 +129,6 @@ class DataFile:
102
129
  """Returns dictionary of all feature flags stored by id"""
103
130
  return self.__feature_flag_by_id
104
131
 
105
- @property
106
- def rule_by_segment_id(self) -> Dict[int, Rule]:
107
- """Returns dictionary of all rule stored by segment id"""
108
- return self.__rule_by_segment_id
109
-
110
132
  @property
111
133
  def rule_info_by_exp_id(self) -> Dict[int, "DataFile.RuleInfo"]:
112
134
  """Returns dictionary of all rule related info stored by experiment id"""
@@ -154,7 +176,6 @@ class DataFile:
154
176
  if feature_flag.rules is not None:
155
177
  has_feature_flag_variable_js_css = self.__has_feature_flag_variable_js_css(feature_flag)
156
178
  for rule in feature_flag.rules:
157
- self.__rule_by_segment_id[rule.segment_id] = rule
158
179
  self.__rule_info_by_exp_id[rule.experiment.id_] = DataFile.RuleInfo(feature_flag, rule)
159
180
  if has_feature_flag_variable_js_css:
160
181
  self.__experiment_ids_with_js_css_variable.add(rule.experiment.id_)
@@ -2,9 +2,11 @@
2
2
 
3
3
  from typing import Any, List, Dict, Optional
4
4
 
5
+ from kameleoon.configuration.custom_data_info import CustomDataInfo
5
6
  from kameleoon.configuration.rule import Rule
6
7
  from kameleoon.configuration.variation import Variation
7
8
  from kameleoon.helpers.string_utils import StringUtils
9
+ from kameleoon.targeting.models import Segment
8
10
 
9
11
 
10
12
  class FeatureFlag:
@@ -12,12 +14,7 @@ class FeatureFlag:
12
14
  FeatureFlag is used for operating feature flags with rules
13
15
  """
14
16
 
15
- @staticmethod
16
- def from_array(array: List[Dict[str, Any]]) -> List["FeatureFlag"]:
17
- """Create a list of FeatureFlags from the json array"""
18
- return [FeatureFlag(item) for item in array]
19
-
20
- def __init__(self, dict_json: Dict[str, Any]) -> None:
17
+ def __init__(self, dict_json: Dict[str, Any], segments: Dict[int, Segment], cdi: CustomDataInfo) -> None:
21
18
  self.id_: int = dict_json.get("id", 0)
22
19
  self.feature_key: str = dict_json.get("featureKey", "")
23
20
  self.default_variation_key: str = dict_json.get("defaultVariationKey", "")
@@ -26,7 +23,9 @@ class FeatureFlag:
26
23
  dict_json.get("variations", [])
27
24
  )
28
25
  self.environment_enabled = dict_json.get("environmentEnabled", False)
29
- self.rules: List[Rule] = Rule.from_array(dict_json.get("rules", []))
26
+ self.rules: List[Rule] = Rule.from_array(dict_json.get("rules", []), segments)
27
+ bucketing_cd_id = dict_json.get("bucketingCustomDataId")
28
+ self.bucketing_custom_data_index = cdi.get_custom_data_index_by_id(bucketing_cd_id) if bucketing_cd_id else None
30
29
 
31
30
  def get_variation(self, key: str) -> Optional[Variation]:
32
31
  """Retrun a variation for the given key"""
@@ -41,6 +40,7 @@ class FeatureFlag:
41
40
  f"variations:{StringUtils.object_to_string(self.variations)},"
42
41
  f"default_variation_key:'{self.default_variation_key}',"
43
42
  f"me_group_name:'{self.me_group_name}',"
44
- f"rules:{len(self.rules)}"
43
+ f"rules:{len(self.rules)},"
44
+ f"bucketing_custom_data_index:{self.bucketing_custom_data_index}"
45
45
  f"}}"
46
46
  )
@@ -12,12 +12,11 @@ class Rule:
12
12
  """
13
13
 
14
14
  @staticmethod
15
- def from_array(array: List[Dict[str, Any]]) -> List["Rule"]:
15
+ def from_array(array: List[Dict[str, Any]], segments: Dict[int, Segment]) -> List["Rule"]:
16
16
  """Create a list of Rules from the json array"""
17
+ return [Rule(item, segments) for item in array]
17
18
 
18
- return [Rule(item) for item in array]
19
-
20
- def __init__(self, dict_json: Dict[str, Any]):
19
+ def __init__(self, dict_json: Dict[str, Any], segments: Dict[int, Segment]) -> None:
21
20
  self.id_: int = dict_json["id"]
22
21
  self.order: int = dict_json.get("order", 0)
23
22
  type_literal: str = dict_json.get("type", "")
@@ -25,21 +24,19 @@ class Rule:
25
24
  self.exposition: float = dict_json.get("exposition", 0.0)
26
25
  self.experiment = Experiment.from_json(dict_json)
27
26
  self.respool_time: Optional[int] = dict_json.get("respoolTime", None)
28
- segment = dict_json.get("segment")
29
- self.targeting_segment: Optional[Segment] = Segment(segment) if segment else None
30
- self.segment_id: int = self.targeting_segment.id_ if self.targeting_segment else -1
27
+ segment_id = dict_json.get("segmentId")
28
+ self.segment_id: int = segment_id or -1
29
+ self.targeting_segment: Optional[Segment] = segments.get(segment_id) if segment_id else None
31
30
 
32
31
  @property
33
32
  def is_experimentation(self) -> bool:
34
33
  """Return `true` if rule is `experimentation` type"""
35
-
36
34
  return self.type == RuleType.EXPERIMENTATION
37
35
 
38
36
  @property
39
37
  def is_targeted_delivery(self) -> bool:
40
38
  """Return `true` if rule is `targeted delivery` type"""
41
-
42
39
  return self.type == RuleType.TARGETED_DELIVERY
43
40
 
44
- def __str__(self):
41
+ def __str__(self) -> str:
45
42
  return f"Rule{{id:{self.id_}}}"
@@ -24,6 +24,7 @@ class DataType(IntEnum):
24
24
  FORCED_FEATURE_VARIATION = 15
25
25
  CBS = 16
26
26
  PERSONALIZATION = 17
27
+ TARGETED_SEGMENT = 18
27
28
 
28
29
 
29
30
  class BaseData:
@@ -62,7 +62,7 @@ class AssignedVariation(BaseData, DuplicationUnsafeSendableBase):
62
62
  """Checks if the assigned variation is valid based on the respool time."""
63
63
  return (respool_time is None) or (self.assignment_time >= respool_time)
64
64
 
65
- def __str__(self):
65
+ def __str__(self) -> str:
66
66
  return (
67
67
  f"AssignedVariation{{experiment_id:{self.__experiment_id},"
68
68
  f"variation_id:{self.__variation_id},"
@@ -18,6 +18,7 @@ from kameleoon.data.mapping_identifier import MappingIdentifier
18
18
  from kameleoon.data.operating_system import OperatingSystem
19
19
  from kameleoon.data.page_view import PageView
20
20
  from kameleoon.data.personalization import Personalization
21
+ from kameleoon.data.targeted_segment import TargetedSegment
21
22
  from kameleoon.data.unique_identifier import UniqueIdentifier
22
23
  from kameleoon.data.user_agent import UserAgent
23
24
  from kameleoon.data.manager.assigned_variation import AssignedVariation
@@ -142,6 +143,13 @@ class Visitor: # pylint: disable=R0904
142
143
  KameleoonLogger.debug('CALL/RETURN: Visitor.personalizations -> (personalizations: %s)', personalizations)
143
144
  return personalizations
144
145
 
146
+ @property
147
+ def targeted_segments(self) -> Dict[int, TargetedSegment]:
148
+ """Returns the personalizations associated with this Visitor."""
149
+ targeted_segments = EMPTY_DICT if self._data.targeted_segments is None else self._data.targeted_segments.copy()
150
+ KameleoonLogger.debug('CALL/RETURN: Visitor.targeted_segments -> (targeted_segments: %s)', targeted_segments)
151
+ return targeted_segments
152
+
145
153
  def get_forced_feature_variation(self, feature_key: str) -> Optional[ForcedFeatureVariation]:
146
154
  """
147
155
  Returns an associated forced feature variation by its feature key if it is present.
@@ -283,6 +291,7 @@ class Visitor: # pylint: disable=R0904
283
291
  self.conversions: Optional[List[Conversion]] = None
284
292
  self.variations: Optional[Dict[int, AssignedVariation]] = None
285
293
  self.personalizations: Optional[Dict[int, Personalization]] = None
294
+ self.targeted_segments: Optional[Dict[int, TargetedSegment]] = None
286
295
  self.forced_variations: Optional[Dict[int, ForcedExperimentVariation]] = None
287
296
  self.simulated_variations: Optional[Dict[str, ForcedFeatureVariation]] = None
288
297
  self.kcs_heat: Optional[KcsHeat] = None
@@ -309,6 +318,8 @@ class Visitor: # pylint: disable=R0904
309
318
  yield from (visit.page_view for visit in list(self.page_view_visits.values()))
310
319
  if self.variations is not None:
311
320
  yield from list(self.variations.values())
321
+ if self.targeted_segments is not None:
322
+ yield from list(self.targeted_segments.values())
312
323
  if self.conversions is not None:
313
324
  yield from list(self.conversions)
314
325
 
@@ -331,6 +342,8 @@ class Visitor: # pylint: disable=R0904
331
342
  count += len(self.page_view_visits)
332
343
  if self.variations is not None:
333
344
  count += len(self.variations)
345
+ if self.targeted_segments is not None:
346
+ count += len(self.targeted_segments)
334
347
  if self.conversions is not None:
335
348
  count += len(self.conversions)
336
349
  return count
@@ -374,6 +387,12 @@ class Visitor: # pylint: disable=R0904
374
387
  if context.overwrite or personalization.id not in self._data.personalizations:
375
388
  self._data.personalizations[personalization.id] = personalization
376
389
 
390
+ def _add_targeted_segment(self, context: DataAddingContext) -> None:
391
+ targeted_segment = cast(TargetedSegment, context.data)
392
+ if self._data.targeted_segments is None:
393
+ self._data.targeted_segments = {}
394
+ self._data.targeted_segments[targeted_segment.id] = targeted_segment
395
+
377
396
  def _add_forced_feature_variation(self, context: DataAddingContext) -> None:
378
397
  ffv = cast(ForcedFeatureVariation, context.data)
379
398
  if self._data.simulated_variations is None:
@@ -471,6 +490,7 @@ class Visitor: # pylint: disable=R0904
471
490
  VisitorVisits: _set_visitor_visits,
472
491
  AssignedVariation: _add_variation,
473
492
  Personalization: _add_personalization,
493
+ TargetedSegment: _add_targeted_segment,
474
494
  ForcedFeatureVariation: _add_forced_feature_variation,
475
495
  ForcedExperimentVariation: _add_forced_experiment_variation,
476
496
  UniqueIdentifier: _set_is_unique_identifier,
@@ -0,0 +1,34 @@
1
+ """Targeted Segment"""
2
+ from kameleoon.data.data import BaseData, DataType
3
+ from kameleoon.network.sendable import DuplicationUnsafeSendableBase
4
+ from kameleoon.network.query_builder import QueryBuilder, QueryParam, QueryParams
5
+
6
+
7
+ class TargetedSegment(BaseData, DuplicationUnsafeSendableBase):
8
+ """Targeted Segment"""
9
+ EVENT_TYPE = "targetingSegment"
10
+
11
+ def __init__(self, id_: int) -> None:
12
+ super().__init__()
13
+ self.__id = id_
14
+
15
+ @property
16
+ def id(self) -> int: # pylint: disable=C0103
17
+ """Returns the segment ID."""
18
+ return self.__id
19
+
20
+ @property
21
+ def data_type(self) -> DataType:
22
+ """Returns the data type for the targeted segment."""
23
+ return DataType.TARGETED_SEGMENT
24
+
25
+ def _add_query_params(self, query_builder: QueryBuilder) -> None:
26
+ # fmt: off
27
+ query_builder.extend(
28
+ QueryParam(QueryParams.EVENT_TYPE, self.EVENT_TYPE),
29
+ QueryParam(QueryParams.SEGMENT_ID, str(self.__id)),
30
+ )
31
+ # fmt: on
32
+
33
+ def __str__(self) -> str:
34
+ return f"TargetedSegment{{id:{self.__id}}}"
@@ -17,6 +17,7 @@ from kameleoon.data.manager.assigned_variation import AssignedVariation
17
17
  from kameleoon.data.manager.forced_experiment_variation import ForcedExperimentVariation
18
18
  from kameleoon.data.manager.visitor import Visitor
19
19
  from kameleoon.data.manager.visitor_manager import VisitorManager
20
+ from kameleoon.data.targeted_segment import TargetedSegment
20
21
 
21
22
  from kameleoon.exceptions import (
22
23
  FeatureVariationNotFound,
@@ -674,7 +675,7 @@ class KameleoonClient:
674
675
  if forced_variation:
675
676
  eval_exp = EvaluatedExperiment.from_forced_variation(forced_variation)
676
677
  elif self.__is_visitor_not_in_holdout(
677
- visitor, visitor_code, track, save
678
+ visitor, visitor_code, track, save, feature_flag.bucketing_custom_data_index
678
679
  ) and self.__is_ff_unrestricted_by_me_group(visitor, visitor_code, feature_flag):
679
680
  eval_exp = self.__calculate_variation_rule_for_feature(visitor_code, feature_flag)
680
681
  if save and not (forced_variation and forced_variation.simulated):
@@ -703,7 +704,7 @@ class KameleoonClient:
703
704
  unrestricted = True
704
705
  me_group = self._data_manager.data_file.me_groups.get(feature_flag.me_group_name)
705
706
  if me_group:
706
- code_for_hash = self.__get_code_for_hash(visitor, visitor_code)
707
+ code_for_hash = self.__get_code_for_hash(visitor, visitor_code, feature_flag.bucketing_custom_data_index)
707
708
  me_group_hash = Hasher.obtain_hash_for_me_group(code_for_hash, feature_flag.me_group_name)
708
709
  KameleoonLogger.debug(
709
710
  "Calculated ME group hash %s for code: %s, meGroup: %s",
@@ -722,20 +723,18 @@ class KameleoonClient:
722
723
  return unrestricted
723
724
 
724
725
  def __is_visitor_not_in_holdout(
725
- self, visitor: Optional[Visitor], visitor_code: str, track: bool, save: bool
726
+ self, visitor: Optional[Visitor], visitor_code: str, track: bool, save: bool, bucketing_cd_index: Optional[int]
726
727
  ) -> bool:
727
728
  holdout = self._data_manager.data_file.holdout
728
729
  if holdout is None:
729
730
  return True
730
731
  in_holdout_variation_key = "in-holdout"
731
732
  KameleoonLogger.debug(
732
- "CALL: KameleoonClient.__is_visitor_not_in_holdout(visitor, visitor_code: %s, track: %s, save: %s)",
733
- visitor_code,
734
- track,
735
- save,
733
+ "CALL: KameleoonClient.__is_visitor_not_in_holdout(visitor, visitor_code: %s, track: %s, save: %s, "
734
+ "bucketing_cd_index: %s)", visitor_code, track, save, bucketing_cd_index
736
735
  )
737
736
  is_not_in_holdout = True
738
- code_for_hash = self.__get_code_for_hash(visitor, visitor_code)
737
+ code_for_hash = self.__get_code_for_hash(visitor, visitor_code, bucketing_cd_index)
739
738
  variation_hash = Hasher.obtain(code_for_hash, holdout.id_)
740
739
  KameleoonLogger.debug("Calculated holdout hash %s for code %s", variation_hash, code_for_hash)
741
740
  var_by_exp = holdout.get_variation(variation_hash)
@@ -745,12 +744,9 @@ class KameleoonClient:
745
744
  eval_exp = EvaluatedExperiment(var_by_exp, holdout, RuleType.EXPERIMENTATION)
746
745
  self.__save_variation(visitor_code, eval_exp)
747
746
  KameleoonLogger.debug(
748
- "RETURN: KameleoonClient.__is_visitor_not_in_holdout(visitor, visitor_code: %s, track: %s, save: %s)"
749
- " -> (is_not_in_holdout: %s)",
750
- visitor_code,
751
- track,
752
- save,
753
- is_not_in_holdout,
747
+ "RETURN: KameleoonClient.__is_visitor_not_in_holdout(visitor, visitor_code: %s, track: %s, save: %s, "
748
+ "bucketing_cd_index: %s) -> (is_not_in_holdout: %s)",
749
+ visitor_code, track, save, bucketing_cd_index, is_not_in_holdout
754
750
  )
755
751
  return is_not_in_holdout
756
752
 
@@ -1385,7 +1381,7 @@ class KameleoonClient:
1385
1381
  campaign_id,
1386
1382
  rule,
1387
1383
  )
1388
- targeting = self._targeting_manager.check_targeting(visitor_code, campaign_id, rule)
1384
+ targeting = self._targeting_manager.check_targeting(visitor_code, campaign_id, rule.targeting_segment)
1389
1385
  KameleoonLogger.debug(
1390
1386
  "RETURN: KameleoonClient.__check_targeting(visitor_code: %s, campaign_id: %s, rule: %s) -> "
1391
1387
  "(targeting: %s)",
@@ -1601,21 +1597,25 @@ class KameleoonClient:
1601
1597
  )
1602
1598
 
1603
1599
  @staticmethod
1604
- def __get_code_for_hash(visitor: Optional[Visitor], visitor_code: str) -> str:
1605
- # use mapping_identifier instead of visitor_code if it was set up
1606
- return (
1607
- visitor.mapping_identifier
1608
- if visitor is not None and visitor.mapping_identifier is not None
1609
- else visitor_code
1610
- )
1600
+ def __get_code_for_hash(visitor: Optional[Visitor], visitor_code: str, bucketing_cd_index: Optional[int]) -> str:
1601
+ if visitor is None:
1602
+ return visitor_code
1603
+ # 1. Try to use the bucketing custom data's value if bucketingCustomDataId is defined
1604
+ if bucketing_cd_index:
1605
+ bucketing_custom_data = visitor.custom_data.get(bucketing_cd_index)
1606
+ if bucketing_custom_data and (len(bucketing_custom_data.values) > 0):
1607
+ return bucketing_custom_data.values[0]
1608
+ # 2. Use mappingIdentifier instead of visitorCode if it was set up
1609
+ return visitor.mapping_identifier or visitor_code
1611
1610
 
1612
1611
  def __evaluate_cbscores(
1613
- self, visitor: Optional[Visitor], visitor_code: str, rule: Rule
1612
+ self, visitor: Optional[Visitor], visitor_code: str, rule: Rule, bucketing_cd_index: Optional[int]
1614
1613
  ) -> Optional[EvaluatedExperiment]:
1615
1614
  if (visitor is None) or (visitor.cbscores is None):
1616
1615
  return None
1617
1616
  KameleoonLogger.debug(
1618
- "CALL: KameleoonClient.__evaluate_cbscores(visitor, visitor_code: %s, rule: %s)", visitor_code, rule
1617
+ "CALL: KameleoonClient.__evaluate_cbscores(visitor, visitor_code: %s, rule: %s, bucketing_cd_index: %s)",
1618
+ visitor_code, rule, bucketing_cd_index
1619
1619
  )
1620
1620
  eval_exp = None
1621
1621
  var_id_group_by_scores = visitor.cbscores.values.get(rule.experiment.id_)
@@ -1633,16 +1633,14 @@ class KameleoonClient:
1633
1633
  size = len(var_by_exp_in_cbs)
1634
1634
  idx = 0
1635
1635
  if size > 1:
1636
- code_for_hash = self.__get_code_for_hash(visitor, visitor_code)
1636
+ code_for_hash = self.__get_code_for_hash(visitor, visitor_code, bucketing_cd_index)
1637
1637
  variation_hash = Hasher.obtain(code_for_hash, rule.experiment.id_, rule.respool_time)
1638
1638
  KameleoonLogger.debug("Calculated CBS hash %s for code %s", variation_hash, code_for_hash)
1639
1639
  idx = min(int(variation_hash * size), size - 1)
1640
1640
  eval_exp = EvaluatedExperiment.from_var_by_exp_rule(var_by_exp_in_cbs[idx], rule)
1641
1641
  KameleoonLogger.debug(
1642
- "RETURN: KameleoonClient.__evaluate_cbscores(visitor, visitor_code: %s, rule: %s) -> (eval_exp: %s)",
1643
- visitor_code,
1644
- rule,
1645
- eval_exp,
1642
+ "RETURN: KameleoonClient.__evaluate_cbscores(visitor, visitor_code: %s, rule: %s, bucketing_cd_index: %s)"
1643
+ " -> (eval_exp: %s)", visitor_code, rule, bucketing_cd_index, eval_exp
1646
1644
  )
1647
1645
  return eval_exp
1648
1646
 
@@ -1656,7 +1654,7 @@ class KameleoonClient:
1656
1654
  feature_flag,
1657
1655
  )
1658
1656
  visitor = self._visitor_manager.get_visitor(visitor_code)
1659
- code_for_hash = self.__get_code_for_hash(visitor, visitor_code)
1657
+ code_for_hash = self.__get_code_for_hash(visitor, visitor_code, feature_flag.bucketing_custom_data_index)
1660
1658
  eval_exp = None
1661
1659
  for rule in feature_flag.rules:
1662
1660
  forced_variation = visitor.get_forced_experiment_variation(rule.experiment.id_) if visitor else None
@@ -1676,7 +1674,9 @@ class KameleoonClient:
1676
1674
  KameleoonLogger.debug("Calculated rule hash %s for code %s", hash_rule, code_for_hash)
1677
1675
  # check main expostion for rule with hashRule
1678
1676
  if hash_rule <= rule.exposition:
1679
- eval_exp = self.__evaluate_cbscores(visitor, visitor_code, rule)
1677
+ eval_exp = self.__evaluate_cbscores(
1678
+ visitor, visitor_code, rule, feature_flag.bucketing_custom_data_index
1679
+ )
1680
1680
  if eval_exp:
1681
1681
  break
1682
1682
  if rule.is_targeted_delivery:
@@ -1783,3 +1783,25 @@ class KameleoonClient:
1783
1783
  variation_key,
1784
1784
  force_targeting,
1785
1785
  )
1786
+
1787
+ def evaluate_audiences(self, visitor_code: str) -> None:
1788
+ """
1789
+ Evaluates the visitor against all available Audiences Explorer segments and tracks those that match.
1790
+ A detailed analysis of segment performance can then be performed directly in Audiences Explorer.
1791
+
1792
+ :param visitor_code: The unique visitor code identifying the visitor.
1793
+ :type visitor_code: str
1794
+
1795
+ :raises VisitorCodeInvalid: The provided **visitor code** is invalid.
1796
+ """
1797
+ KameleoonLogger.info("CALL: KameleoonClient.evaluate_audiences(visitor_code: %s)", visitor_code)
1798
+ validate_visitor_code(visitor_code)
1799
+ segments = [
1800
+ TargetedSegment(seg.id_)
1801
+ for seg in self._data_manager.data_file.audience_tracking_segments
1802
+ if self._targeting_manager.check_targeting(visitor_code, None, seg)
1803
+ ]
1804
+ if segments:
1805
+ self._visitor_manager.add_data(visitor_code, *segments)
1806
+ self._tracking_manager.add_visitor_code(visitor_code)
1807
+ KameleoonLogger.info("RETURN: KameleoonClient.evaluate_audiences(visitor_code: %s)", visitor_code)
@@ -48,6 +48,7 @@ class QueryParams(str, Enum):
48
48
  REVENUE = "revenue"
49
49
  SDK_NAME = "sdkName"
50
50
  SDK_VERSION = "sdkVersion"
51
+ SEGMENT_ID = "id"
51
52
  SITE_CODE = "siteCode"
52
53
  STATIC_DATA = "staticData"
53
54
  TIME_SINCE_PREVIOUS_VISIT = "timeSincePreviousVisit"
@@ -23,7 +23,7 @@ class DataServiceImpl(DataService, ServiceImpl):
23
23
  if timeout is None:
24
24
  timeout = self.network_manager.call_timeout
25
25
  url = self.network_manager.url_provider.make_tracking_url()
26
- request = Request(HttpMethod.POST, url, timeout, body=lines)
26
+ request = Request(HttpMethod.POST, url, timeout, body=lines, headers={"Content-Type": "*/*"})
27
27
  response = self._make_call(
28
28
  request, True, self.NUMBER_OF_RECONNECTION_ON_FAILURE_UNCRITICAL, self._TRACKING_CALL_RETRY_DELAY
29
29
  )
@@ -14,7 +14,7 @@ class UrlProvider:
14
14
  _EXPERIMENTS_CONFIGURATIONS_PATH = "/visit/experimentsConfigurations"
15
15
  _GET_DATA_PATH = "/map/map"
16
16
  _POST_DATA_PATH = "/map/maps"
17
- _CONFIGURATION_API_URL_FORMAT = "https://{0}/{1}"
17
+ _CONFIGURATION_API_URL_FORMAT = "https://{0}/v3/{1}"
18
18
  _RT_CONFIGURATION_URL_FORMAT = "https://{0}:8110/sse?{1}"
19
19
  _ACCESS_TOKEN_URL_FORMAT = "https://{0}/oauth/token"
20
20
  _DATA_API_URL_FORMAT = "https://{0}{1}?{2}"
@@ -4,7 +4,7 @@ from typing import Optional, Tuple
4
4
 
5
5
  from kameleoon.logging.kameleoon_logger import KameleoonLogger
6
6
 
7
- version_info = (3, 13, 0)
7
+ version_info = (3, 15, 0)
8
8
  __version__ = ".".join(str(v) for v in version_info) # uses in scripts
9
9
 
10
10
 
@@ -35,7 +35,9 @@ class ExclusiveExperimentCondition(TargetingCondition):
35
35
  super().__init__(json_condition)
36
36
  self.__campaign_type = json_condition.get("campaignType")
37
37
 
38
- def check(self, data: TargetingData) -> bool:
38
+ def check(self, data: Any) -> bool:
39
+ if not isinstance(data, self.TargetingData):
40
+ return False
39
41
  if self.__campaign_type == self.CampaignType.EXPERIMENT:
40
42
  return self.__check_experiment(data)
41
43
  if self.__campaign_type == self.CampaignType.PERSONALIZATION:
@@ -42,5 +42,5 @@ class SegmentCondition(TargetingCondition):
42
42
  return isinstance(data, SegmentCondition.SegmentInfo) and self.__check(data)
43
43
 
44
44
  def __check(self, segment_info: SegmentInfo) -> bool:
45
- rule = segment_info.data_file().rule_by_segment_id.get(self.__segment_id)
46
- return rule is not None and rule.targeting_segment.check_tree(segment_info.condition_data())
45
+ segment = segment_info.data_file().segments.get(self.__segment_id)
46
+ return segment is not None and segment.check_tree(segment_info.condition_data())
@@ -0,0 +1,28 @@
1
+ """Targeting Segment"""
2
+
3
+ from typing import Any, Dict, Optional
4
+ from kameleoon.targeting.tree_builder import create_tree, Tree
5
+
6
+
7
+ class Segment:
8
+ """Segment with targeting data"""
9
+
10
+ def __init__(self, id_: int, tree: Optional[Tree], audience_tracking: bool) -> None:
11
+ self.id_ = id_
12
+ self.tree = tree
13
+ self.audience_tracking = audience_tracking
14
+
15
+ @staticmethod
16
+ def from_json(obj: Dict[str, Any]) -> "Segment":
17
+ """Creates a Segment instance from JSON object"""
18
+ id_ = obj.get("id", -1)
19
+ tree = create_tree(obj.get("conditionsData"))
20
+ audience_tracking = obj.get("audienceTracking", False)
21
+ return Segment(id_, tree, audience_tracking)
22
+
23
+ def check_tree(self, get_targeting_data) -> Optional[bool]:
24
+ """Checks the targeting throught targeting tree"""
25
+ return self.tree.check(get_targeting_data) if self.tree else True
26
+
27
+ def __str__(self) -> str:
28
+ return f"Segment{{id:{self.id_},audience_tracking:{self.audience_tracking}}}"