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.
- {kameleoon-client-python-3.13.0/kameleoon_client_python.egg-info → kameleoon-client-python-3.15.0}/PKG-INFO +1 -1
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/configuration/custom_data_info.py +7 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/configuration/data_file.py +38 -17
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/configuration/feature_flag.py +8 -8
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/configuration/rule.py +7 -10
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/data.py +1 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/manager/assigned_variation.py +1 -1
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/manager/visitor.py +20 -0
- kameleoon-client-python-3.15.0/kameleoon/data/targeted_segment.py +34 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/kameleoon_client.py +53 -31
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/query_builder.py +1 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/services/data_service_impl.py +1 -1
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/url_provider.py +1 -1
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/sdk_version.py +1 -1
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/exclusive_experiment_condition.py +3 -1
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/segment_condition.py +2 -2
- kameleoon-client-python-3.15.0/kameleoon/targeting/models.py +28 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/targeting_manager.py +16 -14
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0/kameleoon_client_python.egg-info}/PKG-INFO +1 -1
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon_client_python.egg-info/SOURCES.txt +1 -0
- kameleoon-client-python-3.13.0/kameleoon/targeting/models.py +0 -29
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/MANIFEST.in +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/README.md +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/__init__.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/client_readiness/__init__.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/client_readiness/async_readiness.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/client_readiness/threading_readiness.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/configuration/__init__.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/configuration/experiment.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/configuration/me_group.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/configuration/rule_type.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/configuration/settings.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/configuration/variable.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/configuration/variation.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/configuration/variation_by_exposition.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/__init__.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/browser.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/cbscores.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/conversion.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/cookie.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/custom_data.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/device.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/geolocation.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/kcs_heat.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/manager/__init__.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/manager/forced_experiment_variation.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/manager/forced_feature_variation.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/manager/forced_variation.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/manager/page_view_visit.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/manager/visitor_manager.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/manager/visitor_slot.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/mapping_identifier.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/operating_system.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/page_view.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/personalization.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/unique_identifier.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/user_agent.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/visitor_visits.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/defaults.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/exceptions.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/helpers/__init__.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/helpers/domain.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/helpers/functions.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/helpers/hasher.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/helpers/logger.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/helpers/multi_threading.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/helpers/nonce.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/helpers/repeat_timer.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/helpers/scheduler.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/helpers/string_utils.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/helpers/visitor_code.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/hybrid/__init__.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/hybrid/hybrid_manager.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/hybrid/hybrid_manager_impl.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/kameleoon_client_config.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/kameleoon_client_factory.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/logging/__init__.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/logging/kameleoon_logger.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/logging/log_level.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/logging/logger.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/__init__.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/asyncexec/__init__.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/asyncexec/async_executor.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/data/__init__.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/data/data_manager.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/evaluation/__init__.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/evaluation/evaluated_experiment.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/remote_data/__init__.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/remote_data/remote_data_manager.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/remote_data/remote_visitor_data.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/tracking/__init__.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/tracking/tracking_builder.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/tracking/tracking_manager.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/tracking/visitor_tracking_registry.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/warehouse/__init__.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/managers/warehouse/warehouse_manager.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/middleware.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/__init__.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/access_token_source.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/access_token_source_factory.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/activity_event.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/cookie/__init__.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/cookie/cookie_manager.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/net_provider.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/net_provider_impl.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/network_manager.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/network_manager_factory.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/network_manager_factory_impl.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/network_manager_impl.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/query_encodable.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/sendable.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/service_initialize_context.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/service_initialize_context_impl.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/services/__init__.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/services/automation_service.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/services/automation_service_impl.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/services/configuration_service.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/services/configuration_service_impl.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/services/data_service.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/services/service.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/services/service_impl.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/uri_helper.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/real_time/__init__.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/real_time/real_time_configuration_service.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/real_time/real_time_event.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/storage/__init__.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/storage/cache.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/storage/cache_factory.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/storage/cache_factory_impl.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/storage/cache_impl.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/__init__.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/__init__.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/browser_condition.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/constants.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/conversion_condition.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/cookie_condition.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/custom_datum.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/device_condition.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/geolocation_condition.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/kcs_heat_range_condition.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/number_condition.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/operating_system_condition.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/page_title_condition.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/page_url_condition.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/page_view_number_condition.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/previous_page_condition.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/sdk_language_condition.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/string_value_condition.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/target_experiment_condition.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/target_feature_flag_condition.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/target_personalization_condition.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/targeting_condition.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/time_elapsed_since_visit_condition.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/unknown_condition.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/visit_number_today_condition.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/visit_number_total_condition.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/visitor_code_condition.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/conditions/visitor_new_return_condition.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/tree_builder.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/targeting/tree_condition_factory.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/types/__init__.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/types/remote_visitor_data_filter.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/types/variable.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/types/variation.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon_client_python.egg-info/dependency_links.txt +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon_client_python.egg-info/requires.txt +14 -14
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon_client_python.egg-info/top_level.txt +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/setup.cfg +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/setup.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/tests/integration/proj/__init__.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/tests/integration/proj/kameleoon_app/__init__.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/tests/integration/proj/kameleoon_app/admin.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/tests/integration/proj/kameleoon_app/apps.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/tests/integration/proj/kameleoon_app/migrations/__init__.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/tests/integration/proj/kameleoon_app/models.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/tests/integration/proj/kameleoon_app/tests.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/tests/integration/proj/kameleoon_app/views.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/tests/integration/proj/manage.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/tests/integration/proj/proj/__init__.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/tests/integration/proj/proj/asgi.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/tests/integration/proj/proj/settings.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/tests/integration/proj/proj/urls.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/tests/integration/proj/proj/wsgi.py +0 -0
- {kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/tests/resources/config.yml +0 -0
|
@@ -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
|
-
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
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
|
-
|
|
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
|
)
|
{kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/configuration/rule.py
RENAMED
|
@@ -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
|
-
|
|
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
|
-
|
|
29
|
-
self.
|
|
30
|
-
self.
|
|
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_}}}"
|
|
@@ -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},"
|
{kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/data/manager/visitor.py
RENAMED
|
@@ -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}}}"
|
{kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/kameleoon_client.py
RENAMED
|
@@ -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
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
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)",
|
|
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
|
|
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(
|
|
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)
|
|
@@ -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
|
)
|
{kameleoon-client-python-3.13.0 → kameleoon-client-python-3.15.0}/kameleoon/network/url_provider.py
RENAMED
|
@@ -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}"
|
|
@@ -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:
|
|
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
|
-
|
|
46
|
-
return
|
|
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}}}"
|