kameleoon-client-python 3.12.1__tar.gz → 3.14.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 (186) hide show
  1. {kameleoon-client-python-3.12.1/kameleoon_client_python.egg-info → kameleoon-client-python-3.14.0}/PKG-INFO +1 -1
  2. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/configuration/custom_data_info.py +7 -0
  3. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/configuration/data_file.py +38 -13
  4. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/configuration/feature_flag.py +8 -8
  5. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/configuration/rule.py +7 -10
  6. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/data/manager/visitor.py +17 -4
  7. kameleoon-client-python-3.14.0/kameleoon/data/visitor_visits.py +117 -0
  8. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/kameleoon_client.py +48 -45
  9. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/managers/remote_data/remote_data_manager.py +4 -4
  10. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/managers/remote_data/remote_visitor_data.py +25 -15
  11. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/network/net_provider.py +6 -2
  12. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/network/net_provider_impl.py +3 -2
  13. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/network/query_builder.py +17 -15
  14. kameleoon-client-python-3.14.0/kameleoon/network/services/configuration_service.py +24 -0
  15. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/network/services/configuration_service_impl.py +30 -15
  16. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/network/services/data_service_impl.py +1 -1
  17. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/network/url_provider.py +4 -8
  18. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/sdk_version.py +1 -1
  19. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/conditions/targeting_condition.py +0 -1
  20. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/conditions/time_elapsed_since_visit_condition.py +9 -9
  21. kameleoon-client-python-3.14.0/kameleoon/targeting/conditions/visit_number_today_condition.py +40 -0
  22. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/conditions/visit_number_total_condition.py +7 -5
  23. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/conditions/visitor_new_return_condition.py +9 -5
  24. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/targeting_manager.py +6 -7
  25. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/tree_condition_factory.py +0 -4
  26. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0/kameleoon_client_python.egg-info}/PKG-INFO +1 -1
  27. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon_client_python.egg-info/SOURCES.txt +0 -1
  28. kameleoon-client-python-3.12.1/kameleoon/data/visitor_visits.py +0 -32
  29. kameleoon-client-python-3.12.1/kameleoon/network/services/configuration_service.py +0 -13
  30. kameleoon-client-python-3.12.1/kameleoon/targeting/conditions/exclusive_feature_flag_condition.py +0 -42
  31. kameleoon-client-python-3.12.1/kameleoon/targeting/conditions/visit_number_today_condition.py +0 -28
  32. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/MANIFEST.in +0 -0
  33. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/README.md +0 -0
  34. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/__init__.py +0 -0
  35. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/client_readiness/__init__.py +0 -0
  36. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/client_readiness/async_readiness.py +0 -0
  37. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/client_readiness/threading_readiness.py +0 -0
  38. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/configuration/__init__.py +0 -0
  39. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/configuration/experiment.py +0 -0
  40. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/configuration/me_group.py +0 -0
  41. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/configuration/rule_type.py +0 -0
  42. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/configuration/settings.py +0 -0
  43. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/configuration/variable.py +0 -0
  44. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/configuration/variation.py +0 -0
  45. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/configuration/variation_by_exposition.py +0 -0
  46. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/data/__init__.py +0 -0
  47. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/data/browser.py +0 -0
  48. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/data/cbscores.py +0 -0
  49. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/data/conversion.py +0 -0
  50. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/data/cookie.py +0 -0
  51. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/data/custom_data.py +0 -0
  52. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/data/data.py +0 -0
  53. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/data/device.py +0 -0
  54. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/data/geolocation.py +0 -0
  55. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/data/kcs_heat.py +0 -0
  56. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/data/manager/__init__.py +0 -0
  57. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/data/manager/assigned_variation.py +0 -0
  58. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/data/manager/forced_experiment_variation.py +0 -0
  59. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/data/manager/forced_feature_variation.py +0 -0
  60. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/data/manager/forced_variation.py +0 -0
  61. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/data/manager/page_view_visit.py +0 -0
  62. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/data/manager/visitor_manager.py +0 -0
  63. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/data/manager/visitor_slot.py +0 -0
  64. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/data/mapping_identifier.py +0 -0
  65. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/data/operating_system.py +0 -0
  66. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/data/page_view.py +0 -0
  67. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/data/personalization.py +0 -0
  68. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/data/unique_identifier.py +0 -0
  69. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/data/user_agent.py +0 -0
  70. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/defaults.py +0 -0
  71. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/exceptions.py +0 -0
  72. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/helpers/__init__.py +0 -0
  73. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/helpers/domain.py +0 -0
  74. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/helpers/functions.py +0 -0
  75. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/helpers/hasher.py +0 -0
  76. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/helpers/logger.py +0 -0
  77. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/helpers/multi_threading.py +0 -0
  78. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/helpers/nonce.py +0 -0
  79. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/helpers/repeat_timer.py +0 -0
  80. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/helpers/scheduler.py +0 -0
  81. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/helpers/string_utils.py +0 -0
  82. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/helpers/visitor_code.py +0 -0
  83. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/hybrid/__init__.py +0 -0
  84. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/hybrid/hybrid_manager.py +0 -0
  85. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/hybrid/hybrid_manager_impl.py +0 -0
  86. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/kameleoon_client_config.py +0 -0
  87. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/kameleoon_client_factory.py +0 -0
  88. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/logging/__init__.py +0 -0
  89. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/logging/kameleoon_logger.py +0 -0
  90. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/logging/log_level.py +0 -0
  91. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/logging/logger.py +0 -0
  92. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/managers/__init__.py +0 -0
  93. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/managers/asyncexec/__init__.py +0 -0
  94. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/managers/asyncexec/async_executor.py +0 -0
  95. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/managers/data/__init__.py +0 -0
  96. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/managers/data/data_manager.py +0 -0
  97. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/managers/evaluation/__init__.py +0 -0
  98. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/managers/evaluation/evaluated_experiment.py +0 -0
  99. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/managers/remote_data/__init__.py +0 -0
  100. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/managers/tracking/__init__.py +0 -0
  101. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/managers/tracking/tracking_builder.py +0 -0
  102. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/managers/tracking/tracking_manager.py +0 -0
  103. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/managers/tracking/visitor_tracking_registry.py +0 -0
  104. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/managers/warehouse/__init__.py +0 -0
  105. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/managers/warehouse/warehouse_manager.py +0 -0
  106. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/middleware.py +0 -0
  107. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/network/__init__.py +0 -0
  108. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/network/access_token_source.py +0 -0
  109. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/network/access_token_source_factory.py +0 -0
  110. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/network/activity_event.py +0 -0
  111. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/network/cookie/__init__.py +0 -0
  112. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/network/cookie/cookie_manager.py +0 -0
  113. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/network/network_manager.py +0 -0
  114. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/network/network_manager_factory.py +0 -0
  115. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/network/network_manager_factory_impl.py +0 -0
  116. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/network/network_manager_impl.py +0 -0
  117. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/network/query_encodable.py +0 -0
  118. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/network/sendable.py +0 -0
  119. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/network/service_initialize_context.py +0 -0
  120. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/network/service_initialize_context_impl.py +0 -0
  121. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/network/services/__init__.py +0 -0
  122. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/network/services/automation_service.py +0 -0
  123. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/network/services/automation_service_impl.py +0 -0
  124. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/network/services/data_service.py +0 -0
  125. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/network/services/service.py +0 -0
  126. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/network/services/service_impl.py +0 -0
  127. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/network/uri_helper.py +0 -0
  128. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/real_time/__init__.py +0 -0
  129. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/real_time/real_time_configuration_service.py +0 -0
  130. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/real_time/real_time_event.py +0 -0
  131. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/storage/__init__.py +0 -0
  132. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/storage/cache.py +0 -0
  133. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/storage/cache_factory.py +0 -0
  134. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/storage/cache_factory_impl.py +0 -0
  135. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/storage/cache_impl.py +0 -0
  136. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/__init__.py +0 -0
  137. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/conditions/__init__.py +0 -0
  138. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/conditions/browser_condition.py +0 -0
  139. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/conditions/constants.py +0 -0
  140. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/conditions/conversion_condition.py +0 -0
  141. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/conditions/cookie_condition.py +0 -0
  142. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/conditions/custom_datum.py +0 -0
  143. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/conditions/device_condition.py +0 -0
  144. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/conditions/exclusive_experiment_condition.py +0 -0
  145. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/conditions/geolocation_condition.py +0 -0
  146. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/conditions/kcs_heat_range_condition.py +0 -0
  147. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/conditions/number_condition.py +0 -0
  148. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/conditions/operating_system_condition.py +0 -0
  149. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/conditions/page_title_condition.py +0 -0
  150. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/conditions/page_url_condition.py +0 -0
  151. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/conditions/page_view_number_condition.py +0 -0
  152. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/conditions/previous_page_condition.py +0 -0
  153. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/conditions/sdk_language_condition.py +0 -0
  154. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/conditions/segment_condition.py +0 -0
  155. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/conditions/string_value_condition.py +0 -0
  156. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/conditions/target_experiment_condition.py +0 -0
  157. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/conditions/target_feature_flag_condition.py +0 -0
  158. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/conditions/target_personalization_condition.py +0 -0
  159. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/conditions/unknown_condition.py +0 -0
  160. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/conditions/visitor_code_condition.py +0 -0
  161. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/models.py +0 -0
  162. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/targeting/tree_builder.py +0 -0
  163. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/types/__init__.py +0 -0
  164. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/types/remote_visitor_data_filter.py +0 -0
  165. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/types/variable.py +0 -0
  166. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon/types/variation.py +0 -0
  167. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon_client_python.egg-info/dependency_links.txt +0 -0
  168. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon_client_python.egg-info/requires.txt +13 -13
  169. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/kameleoon_client_python.egg-info/top_level.txt +0 -0
  170. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/setup.cfg +0 -0
  171. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/setup.py +0 -0
  172. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/tests/integration/proj/__init__.py +0 -0
  173. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/tests/integration/proj/kameleoon_app/__init__.py +0 -0
  174. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/tests/integration/proj/kameleoon_app/admin.py +0 -0
  175. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/tests/integration/proj/kameleoon_app/apps.py +0 -0
  176. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/tests/integration/proj/kameleoon_app/migrations/__init__.py +0 -0
  177. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/tests/integration/proj/kameleoon_app/models.py +0 -0
  178. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/tests/integration/proj/kameleoon_app/tests.py +0 -0
  179. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/tests/integration/proj/kameleoon_app/views.py +0 -0
  180. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/tests/integration/proj/manage.py +0 -0
  181. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/tests/integration/proj/proj/__init__.py +0 -0
  182. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/tests/integration/proj/proj/asgi.py +0 -0
  183. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/tests/integration/proj/proj/settings.py +0 -0
  184. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/tests/integration/proj/proj/urls.py +0 -0
  185. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.0}/tests/integration/proj/proj/wsgi.py +0 -0
  186. {kameleoon-client-python-3.12.1 → kameleoon-client-python-3.14.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.12.1
3
+ Version: 3.14.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,38 +20,55 @@ 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, Settings(), {}, CustomDataInfo(None))
23
+ data_file = DataFile(environment, None, Settings(), {}, CustomDataInfo(None))
23
24
  return data_file
24
25
 
25
26
  @staticmethod
26
27
  def from_json(
27
- environment: Optional[str], configuration: Dict[str, Any]
28
+ environment: Optional[str], last_modified: Optional[str], configuration: Dict[str, Any]
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, settings, feature_flags, custom_data_info, holdout)
36
+ data_file = DataFile(environment, last_modified, settings, 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(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],
59
+ last_modified: Optional[str],
44
60
  settings: Settings,
45
61
  feature_flags: Dict[str, FeatureFlag],
46
62
  custom_data_info: CustomDataInfo,
47
63
  holdout: Optional[Experiment] = None,
48
64
  ) -> None:
49
65
  KameleoonLogger.debug(
50
- 'CALL: DataFile(environment: %s, settings: %s, feature_flags: %s, custom_data_info: %s, holdout: %s)',
51
- environment, settings, feature_flags, custom_data_info, holdout
66
+ "CALL: DataFile(environment: %s, last_modified: %s, settings: %s, feature_flags: %s,"
67
+ " custom_data_info: %s, holdout: %s)",
68
+ environment, last_modified, settings, feature_flags, custom_data_info, holdout
52
69
  )
53
70
  self.__environment = environment # pylint: disable=W0238
71
+ self.__last_modified = last_modified
54
72
  self.__settings = settings
55
73
  self.__feature_flags: Dict[str, FeatureFlag] = feature_flags
56
74
  self.__me_groups = self.__make_me_groups(feature_flags.values())
@@ -69,10 +87,16 @@ class DataFile:
69
87
  self.__custom_data_info = custom_data_info
70
88
  self.__holdout = holdout
71
89
  KameleoonLogger.debug(
72
- 'RETURN: DataFile.new(environment: %s, settings: %s, feature_flags: %s, custom_data_info: %s, holdout: %s)',
73
- environment, settings, feature_flags, custom_data_info
90
+ "RETURN: DataFile(environment: %s, last_modified: %s, settings: %s, feature_flags: %s,"
91
+ " custom_data_info: %s, holdout: %s)",
92
+ environment, last_modified, settings, feature_flags, custom_data_info, holdout
74
93
  )
75
94
 
95
+ @property
96
+ def last_modified(self) -> Optional[str]:
97
+ """Returns last modified string"""
98
+ return self.__last_modified
99
+
76
100
  @property
77
101
  def settings(self) -> Settings:
78
102
  """Returns settings"""
@@ -177,11 +201,12 @@ class DataFile:
177
201
 
178
202
  def __str__(self):
179
203
  return (
180
- f"DataFile{{"
204
+ "DataFile{"
181
205
  f"environment:'{self.__environment}',"
206
+ f"last_modified:'{self.__last_modified}',"
182
207
  f"feature_flags:{len(self.__feature_flags)},"
183
208
  f"settings:{self.__settings}"
184
- f"}}"
209
+ "}"
185
210
  )
186
211
 
187
212
  class RuleInfo:
@@ -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_}}}"
@@ -1,4 +1,6 @@
1
1
  """Visitor"""
2
+ import time
3
+
2
4
  from threading import Lock
3
5
  from typing import Any, Callable, Optional, Dict, List, Iterator, Iterable, cast
4
6
 
@@ -44,6 +46,11 @@ class Visitor: # pylint: disable=R0904
44
46
  """Creates and returns a clone of the current Visitor instance which shares all the visitor data."""
45
47
  return Visitor(self)
46
48
 
49
+ @property
50
+ def time_started(self) -> int:
51
+ """Returns the current visit's start time"""
52
+ return self._data.time_started
53
+
47
54
  def enumerate_sendable_data(self) -> Iterator[Sendable]:
48
55
  """Enumerates all sendable data associated with this Visitor."""
49
56
  KameleoonLogger.debug('CALL: Visitor.enumerate_sendable_data()')
@@ -263,6 +270,7 @@ class Visitor: # pylint: disable=R0904
263
270
  class VisitorData:
264
271
  """Visitor Data"""
265
272
  def __init__(self) -> None:
273
+ self.time_started = int(time.time() * 1000)
266
274
  self.lock = Lock()
267
275
  self.user_agent: Optional[str] = None
268
276
  self.device: Optional[Device] = None
@@ -293,6 +301,8 @@ class Visitor: # pylint: disable=R0904
293
301
  yield self.operating_system
294
302
  if self.geolocation:
295
303
  yield self.geolocation
304
+ if self.visitor_visits:
305
+ yield self.visitor_visits
296
306
  if self.custom_data_dict is not None:
297
307
  yield from list(self.custom_data_dict.values())
298
308
  if self.page_view_visits is not None:
@@ -309,18 +319,20 @@ class Visitor: # pylint: disable=R0904
309
319
  count += 1
310
320
  if self.browser is not None:
311
321
  count += 1
322
+ if self.operating_system is not None:
323
+ count += 1
312
324
  if self.geolocation is not None:
313
325
  count += 1
314
- if self.operating_system is not None:
326
+ if self.visitor_visits:
315
327
  count += 1
316
328
  if self.custom_data_dict is not None:
317
329
  count += len(self.custom_data_dict)
318
330
  if self.page_view_visits is not None:
319
331
  count += len(self.page_view_visits)
320
- if self.conversions is not None:
321
- count += len(self.conversions)
322
332
  if self.variations is not None:
323
333
  count += len(self.variations)
334
+ if self.conversions is not None:
335
+ count += len(self.conversions)
324
336
  return count
325
337
 
326
338
  class DataAddingContext:
@@ -433,7 +445,8 @@ class Visitor: # pylint: disable=R0904
433
445
  self._data.cbscores = cast(CBScores, context.data)
434
446
 
435
447
  def _set_visitor_visits(self, context: DataAddingContext) -> None:
436
- self._data.visitor_visits = cast(VisitorVisits, context.data)
448
+ if context.overwrite or (self._data.visitor_visits is None):
449
+ self._data.visitor_visits = cast(VisitorVisits, context.data).localize(self._data.time_started)
437
450
 
438
451
  def _set_is_unique_identifier(self, context: DataAddingContext) -> None:
439
452
  self._is_unique_identifier = cast(UniqueIdentifier, context.data).value
@@ -0,0 +1,117 @@
1
+ """Visitor visits"""
2
+
3
+ from typing import Any, List, Optional, Tuple
4
+
5
+ from kameleoon.data import DataType
6
+ from kameleoon.network.sendable import DuplicationUnsafeSendableBase
7
+ from kameleoon.network.query_builder import QueryBuilder, QueryParam, QueryParams
8
+
9
+ from kameleoon.data.data import BaseData
10
+
11
+
12
+ class VisitorVisits(BaseData, DuplicationUnsafeSendableBase):
13
+ """Visitor visits"""
14
+
15
+ EVENT_TYPE = "staticData"
16
+
17
+ def __init__(
18
+ self, prev_visits: List["Visit"], visit_number=0, *, time_started=0, time_since_previous_visit=0
19
+ ) -> None:
20
+ super().__init__()
21
+ self.__visit_number = max(visit_number, len(prev_visits))
22
+ self.__prev_visits = prev_visits
23
+ self.__time_started = time_started
24
+ self.__time_since_previous_visit = time_since_previous_visit
25
+
26
+ @property
27
+ def data_type(self) -> DataType:
28
+ return DataType.VISITOR_VISITS
29
+
30
+ @property
31
+ def visit_number(self) -> int:
32
+ """Returns the visit number"""
33
+ return self.__visit_number
34
+
35
+ @property
36
+ def prev_visits(self) -> List["Visit"]:
37
+ """Returns the previous visit list"""
38
+ return self.__prev_visits
39
+
40
+ @property
41
+ def time_started(self) -> int:
42
+ """Returns the current visit's start time"""
43
+ return self.__time_started
44
+
45
+ @property
46
+ def time_since_previous_visit(self) -> int:
47
+ """Returns the time since the previous visit"""
48
+ return self.__time_since_previous_visit
49
+
50
+ def localize(self, time_started: int) -> "VisitorVisits":
51
+ """
52
+ Localizes a remote instance of `VisitorVisits` creating a new instance
53
+ with a specific current visit's start time.
54
+ """
55
+ time_since_previous_visit = 0
56
+ for visit in self.__prev_visits:
57
+ time_delta = time_started - visit.time_last_activity
58
+ if time_delta >= 0:
59
+ time_since_previous_visit = time_delta
60
+ break
61
+ return VisitorVisits(
62
+ self.__prev_visits, self.__visit_number,
63
+ time_started=time_started, time_since_previous_visit=time_since_previous_visit,
64
+ )
65
+
66
+ def _add_query_params(self, query_builder: QueryBuilder) -> None:
67
+ query_builder.extend(
68
+ QueryParam(QueryParams.EVENT_TYPE, self.EVENT_TYPE),
69
+ QueryParam(QueryParams.VISIT_NUMBER, str(self.__visit_number)),
70
+ QueryParam(QueryParams.TIME_SINCE_PREVIOUS_VISIT, str(self.__time_since_previous_visit)),
71
+ )
72
+
73
+ def __eq__(self, value: Any) -> bool:
74
+ return isinstance(value, VisitorVisits) and (self.__visit_number == value.visit_number) and \
75
+ (self.__prev_visits == value.prev_visits) and (self.__time_started == value.time_started) and \
76
+ (self.__time_since_previous_visit == value.time_since_previous_visit)
77
+
78
+ def __str__(self) -> str:
79
+ return (
80
+ "VisitorVisits{"
81
+ f"visit_number:{self.__visit_number},"
82
+ f"prev_visits:{[str(v) for v in self.__prev_visits]},"
83
+ f"time_started:{self.__time_started},"
84
+ f"time_since_previous_visit:{self.__time_since_previous_visit}"
85
+ "}"
86
+ )
87
+
88
+ @staticmethod
89
+ def get_visitor_visits(obj: Any) -> Tuple["VisitorVisits", bool]:
90
+ """Determines if an object is `VisitorVisits` or `None` and returns a valid instance of `VisitorVisits`."""
91
+ if isinstance(obj, VisitorVisits):
92
+ return obj, True
93
+ return VisitorVisits([]), obj is None
94
+
95
+ class Visit:
96
+ """Visit"""
97
+
98
+ def __init__(self, time_started: int, time_last_activity: Optional[int] = None) -> None:
99
+ self.__time_started = time_started
100
+ self.__time_last_activity = time_last_activity or time_started
101
+
102
+ @property
103
+ def time_started(self) -> int:
104
+ """Returns the start time"""
105
+ return self.__time_started
106
+
107
+ @property
108
+ def time_last_activity(self) -> int:
109
+ """Returns the last activity time"""
110
+ return self.__time_last_activity
111
+
112
+ def __eq__(self, value: Any) -> bool:
113
+ return isinstance(value, VisitorVisits.Visit) and (self.__time_started == value.time_started) and \
114
+ (self.__time_last_activity == value.time_last_activity)
115
+
116
+ def __str__(self) -> str:
117
+ return f"Visit{{time_started:{self.__time_started},time_last_activity:{self.__time_last_activity}}}"
@@ -47,7 +47,7 @@ from kameleoon.network.net_provider_impl import NetProviderImpl
47
47
  from kameleoon.network.network_manager_factory import NetworkManagerFactory
48
48
  from kameleoon.network.network_manager_factory_impl import NetworkManagerFactoryImpl
49
49
  from kameleoon.network.sendable import Sendable
50
- from kameleoon.network.services.configuration_service import ConfigurationService
50
+ from kameleoon.network.services.configuration_service import ConfigurationService, FetchedConfiguration
51
51
  from kameleoon.network.cookie.cookie_manager import CookieManager
52
52
 
53
53
  from kameleoon.real_time.real_time_configuration_service import RealTimeConfigurationService
@@ -674,7 +674,7 @@ class KameleoonClient:
674
674
  if forced_variation:
675
675
  eval_exp = EvaluatedExperiment.from_forced_variation(forced_variation)
676
676
  elif self.__is_visitor_not_in_holdout(
677
- visitor, visitor_code, track, save
677
+ visitor, visitor_code, track, save, feature_flag.bucketing_custom_data_index
678
678
  ) and self.__is_ff_unrestricted_by_me_group(visitor, visitor_code, feature_flag):
679
679
  eval_exp = self.__calculate_variation_rule_for_feature(visitor_code, feature_flag)
680
680
  if save and not (forced_variation and forced_variation.simulated):
@@ -703,7 +703,7 @@ class KameleoonClient:
703
703
  unrestricted = True
704
704
  me_group = self._data_manager.data_file.me_groups.get(feature_flag.me_group_name)
705
705
  if me_group:
706
- code_for_hash = self.__get_code_for_hash(visitor, visitor_code)
706
+ code_for_hash = self.__get_code_for_hash(visitor, visitor_code, feature_flag.bucketing_custom_data_index)
707
707
  me_group_hash = Hasher.obtain_hash_for_me_group(code_for_hash, feature_flag.me_group_name)
708
708
  KameleoonLogger.debug(
709
709
  "Calculated ME group hash %s for code: %s, meGroup: %s",
@@ -722,20 +722,18 @@ class KameleoonClient:
722
722
  return unrestricted
723
723
 
724
724
  def __is_visitor_not_in_holdout(
725
- self, visitor: Optional[Visitor], visitor_code: str, track: bool, save: bool
725
+ self, visitor: Optional[Visitor], visitor_code: str, track: bool, save: bool, bucketing_cd_index: Optional[int]
726
726
  ) -> bool:
727
727
  holdout = self._data_manager.data_file.holdout
728
728
  if holdout is None:
729
729
  return True
730
730
  in_holdout_variation_key = "in-holdout"
731
731
  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,
732
+ "CALL: KameleoonClient.__is_visitor_not_in_holdout(visitor, visitor_code: %s, track: %s, save: %s, "
733
+ "bucketing_cd_index: %s)", visitor_code, track, save, bucketing_cd_index
736
734
  )
737
735
  is_not_in_holdout = True
738
- code_for_hash = self.__get_code_for_hash(visitor, visitor_code)
736
+ code_for_hash = self.__get_code_for_hash(visitor, visitor_code, bucketing_cd_index)
739
737
  variation_hash = Hasher.obtain(code_for_hash, holdout.id_)
740
738
  KameleoonLogger.debug("Calculated holdout hash %s for code %s", variation_hash, code_for_hash)
741
739
  var_by_exp = holdout.get_variation(variation_hash)
@@ -745,12 +743,9 @@ class KameleoonClient:
745
743
  eval_exp = EvaluatedExperiment(var_by_exp, holdout, RuleType.EXPERIMENTATION)
746
744
  self.__save_variation(visitor_code, eval_exp)
747
745
  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,
746
+ "RETURN: KameleoonClient.__is_visitor_not_in_holdout(visitor, visitor_code: %s, track: %s, save: %s, "
747
+ "bucketing_cd_index: %s) -> (is_not_in_holdout: %s)",
748
+ visitor_code, track, save, bucketing_cd_index, is_not_in_holdout
754
749
  )
755
750
  return is_not_in_holdout
756
751
 
@@ -1435,13 +1430,18 @@ class KameleoonClient:
1435
1430
  KameleoonLogger.debug("CALL: KameleoonClient._fetch_configuration(time_stamp: %s)", time_stamp)
1436
1431
  success = False
1437
1432
  try:
1438
- configuration_json = await self._obtain_configuration(time_stamp)
1439
- if configuration_json:
1440
- data_file = DataFile.from_json(self._config.environment, configuration_json)
1441
- self._data_manager.data_file = data_file
1442
- self._network_manager.url_provider.apply_data_api_domain(data_file.settings.data_api_domain)
1443
- self._call_update_handler_if_needed(time_stamp is not None)
1444
- success = True
1433
+ fetched_configuration = await self._obtain_configuration(time_stamp)
1434
+ if fetched_configuration:
1435
+ if fetched_configuration.configuration:
1436
+ data_file = DataFile.from_json(
1437
+ self._config.environment,
1438
+ fetched_configuration.last_modified,
1439
+ fetched_configuration.configuration
1440
+ )
1441
+ self._data_manager.data_file = data_file
1442
+ self._network_manager.url_provider.apply_data_api_domain(data_file.settings.data_api_domain)
1443
+ self._call_update_handler_if_needed(time_stamp is not None)
1444
+ success = True
1445
1445
  except Exception as ex:
1446
1446
  KameleoonLogger.error("Error occurred during configuration fetching: %s", ex)
1447
1447
  self._manage_configuration_update(self._data_manager.data_file.settings.real_time_update)
@@ -1532,7 +1532,7 @@ class KameleoonClient:
1532
1532
  if not self._data_manager.data_file.settings.real_time_update:
1533
1533
  await self._fetch_configuration()
1534
1534
 
1535
- async def _obtain_configuration(self, time_stamp: Optional[int]) -> Optional[Dict[str, Any]]:
1535
+ async def _obtain_configuration(self, time_stamp: Optional[int]) -> Optional[FetchedConfiguration]:
1536
1536
  """
1537
1537
  Obtaining configuration from CDN service.
1538
1538
  Should be run in a separate thead.
@@ -1541,15 +1541,14 @@ class KameleoonClient:
1541
1541
  :return: None
1542
1542
  """
1543
1543
  KameleoonLogger.debug("CALL: KameleoonClient._obtain_configuration(time_stamp: %s)", time_stamp)
1544
+ last_modified = self._data_manager.data_file.last_modified
1544
1545
  service: ConfigurationService = self._network_manager.get_service(ConfigurationService)
1545
- response = await service.fetch_configuration(self._config.environment, time_stamp)
1546
- content = None
1547
- if response.code and (response.code // 100 == 2):
1548
- content = response.content
1546
+ fetched_configuration = await service.fetch_configuration(self._config.environment, time_stamp, last_modified)
1549
1547
  KameleoonLogger.debug(
1550
- "RETURN: KameleoonClient._obtain_configuration(time_stamp: %s) -> (content: %s)", time_stamp, content
1548
+ "RETURN: KameleoonClient._obtain_configuration(time_stamp: %s) -> (fetched_configuration: %s)",
1549
+ time_stamp, fetched_configuration
1551
1550
  )
1552
- return content
1551
+ return fetched_configuration
1553
1552
 
1554
1553
  def __get_feature_variation_key(self, visitor_code: str, feature_key: str) -> Tuple[FeatureFlag, str]:
1555
1554
  """
@@ -1597,21 +1596,25 @@ class KameleoonClient:
1597
1596
  )
1598
1597
 
1599
1598
  @staticmethod
1600
- def __get_code_for_hash(visitor: Optional[Visitor], visitor_code: str) -> str:
1601
- # use mapping_identifier instead of visitor_code if it was set up
1602
- return (
1603
- visitor.mapping_identifier
1604
- if visitor is not None and visitor.mapping_identifier is not None
1605
- else visitor_code
1606
- )
1599
+ def __get_code_for_hash(visitor: Optional[Visitor], visitor_code: str, bucketing_cd_index: Optional[int]) -> str:
1600
+ if visitor is None:
1601
+ return visitor_code
1602
+ # 1. Try to use the bucketing custom data's value if bucketingCustomDataId is defined
1603
+ if bucketing_cd_index:
1604
+ bucketing_custom_data = visitor.custom_data.get(bucketing_cd_index)
1605
+ if bucketing_custom_data and (len(bucketing_custom_data.values) > 0):
1606
+ return bucketing_custom_data.values[0]
1607
+ # 2. Use mappingIdentifier instead of visitorCode if it was set up
1608
+ return visitor.mapping_identifier or visitor_code
1607
1609
 
1608
1610
  def __evaluate_cbscores(
1609
- self, visitor: Optional[Visitor], visitor_code: str, rule: Rule
1611
+ self, visitor: Optional[Visitor], visitor_code: str, rule: Rule, bucketing_cd_index: Optional[int]
1610
1612
  ) -> Optional[EvaluatedExperiment]:
1611
1613
  if (visitor is None) or (visitor.cbscores is None):
1612
1614
  return None
1613
1615
  KameleoonLogger.debug(
1614
- "CALL: KameleoonClient.__evaluate_cbscores(visitor, visitor_code: %s, rule: %s)", visitor_code, rule
1616
+ "CALL: KameleoonClient.__evaluate_cbscores(visitor, visitor_code: %s, rule: %s, bucketing_cd_index: %s)",
1617
+ visitor_code, rule, bucketing_cd_index
1615
1618
  )
1616
1619
  eval_exp = None
1617
1620
  var_id_group_by_scores = visitor.cbscores.values.get(rule.experiment.id_)
@@ -1629,16 +1632,14 @@ class KameleoonClient:
1629
1632
  size = len(var_by_exp_in_cbs)
1630
1633
  idx = 0
1631
1634
  if size > 1:
1632
- code_for_hash = self.__get_code_for_hash(visitor, visitor_code)
1635
+ code_for_hash = self.__get_code_for_hash(visitor, visitor_code, bucketing_cd_index)
1633
1636
  variation_hash = Hasher.obtain(code_for_hash, rule.experiment.id_, rule.respool_time)
1634
1637
  KameleoonLogger.debug("Calculated CBS hash %s for code %s", variation_hash, code_for_hash)
1635
1638
  idx = min(int(variation_hash * size), size - 1)
1636
1639
  eval_exp = EvaluatedExperiment.from_var_by_exp_rule(var_by_exp_in_cbs[idx], rule)
1637
1640
  KameleoonLogger.debug(
1638
- "RETURN: KameleoonClient.__evaluate_cbscores(visitor, visitor_code: %s, rule: %s) -> (eval_exp: %s)",
1639
- visitor_code,
1640
- rule,
1641
- eval_exp,
1641
+ "RETURN: KameleoonClient.__evaluate_cbscores(visitor, visitor_code: %s, rule: %s, bucketing_cd_index: %s)"
1642
+ " -> (eval_exp: %s)", visitor_code, rule, bucketing_cd_index, eval_exp
1642
1643
  )
1643
1644
  return eval_exp
1644
1645
 
@@ -1652,7 +1653,7 @@ class KameleoonClient:
1652
1653
  feature_flag,
1653
1654
  )
1654
1655
  visitor = self._visitor_manager.get_visitor(visitor_code)
1655
- code_for_hash = self.__get_code_for_hash(visitor, visitor_code)
1656
+ code_for_hash = self.__get_code_for_hash(visitor, visitor_code, feature_flag.bucketing_custom_data_index)
1656
1657
  eval_exp = None
1657
1658
  for rule in feature_flag.rules:
1658
1659
  forced_variation = visitor.get_forced_experiment_variation(rule.experiment.id_) if visitor else None
@@ -1672,7 +1673,9 @@ class KameleoonClient:
1672
1673
  KameleoonLogger.debug("Calculated rule hash %s for code %s", hash_rule, code_for_hash)
1673
1674
  # check main expostion for rule with hashRule
1674
1675
  if hash_rule <= rule.exposition:
1675
- eval_exp = self.__evaluate_cbscores(visitor, visitor_code, rule)
1676
+ eval_exp = self.__evaluate_cbscores(
1677
+ visitor, visitor_code, rule, feature_flag.bucketing_custom_data_index
1678
+ )
1676
1679
  if eval_exp:
1677
1680
  break
1678
1681
  if rule.is_targeted_delivery:
@@ -63,7 +63,7 @@ class RemoteDataManager:
63
63
  if response.content is None:
64
64
  return []
65
65
  custom_data_info = self._data_manager.data_file.custom_data_info
66
- remote_visitor_data = RemoteDataManager.__parse_remote_visitor_data(response.content)
66
+ remote_visitor_data = RemoteDataManager.__parse_remote_visitor_data(response.content, data_filter)
67
67
  remote_visitor_data.mark_data_as_sent(custom_data_info)
68
68
  data_to_add = remote_visitor_data.collect_data_to_add()
69
69
  if add_data and data_to_add:
@@ -77,9 +77,9 @@ class RemoteDataManager:
77
77
  return remote_visitor_data.collect_data_to_return()
78
78
 
79
79
  @staticmethod
80
- def __parse_remote_visitor_data(raw: Any) -> RemoteVisitorData:
80
+ def __parse_remote_visitor_data(raw: Any, data_filter: RemoteVisitorDataFilter) -> RemoteVisitorData:
81
81
  try:
82
- return RemoteVisitorData(raw)
82
+ return RemoteVisitorData(raw, data_filter)
83
83
  except Exception as ex: # pylint: disable=W0703
84
84
  KameleoonLogger.error("Parsing of remote visitor data failed: %s", ex)
85
- return RemoteVisitorData({})
85
+ return RemoteVisitorData({}, data_filter)