kameleoon-client-python 3.14.0__tar.gz → 3.16.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.14.0/kameleoon_client_python.egg-info → kameleoon-client-python-3.16.0}/PKG-INFO +1 -1
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/configuration/custom_data_info.py +8 -1
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/configuration/data_file.py +20 -14
- kameleoon-client-python-3.16.0/kameleoon/data/custom_data.py +90 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/data/data.py +1 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/data/manager/assigned_variation.py +1 -1
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/data/manager/visitor.py +24 -2
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/data/manager/visitor_manager.py +8 -3
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/data/mapping_identifier.py +2 -1
- kameleoon-client-python-3.16.0/kameleoon/data/targeted_segment.py +34 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/kameleoon_client.py +24 -1
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/managers/remote_data/remote_visitor_data.py +1 -1
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/middleware.py +0 -1
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/network/query_builder.py +1 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/sdk_version.py +1 -1
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/conditions/exclusive_experiment_condition.py +3 -1
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/conditions/segment_condition.py +2 -2
- kameleoon-client-python-3.16.0/kameleoon/targeting/models.py +28 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/targeting_manager.py +17 -15
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0/kameleoon_client_python.egg-info}/PKG-INFO +1 -1
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon_client_python.egg-info/SOURCES.txt +1 -0
- kameleoon-client-python-3.14.0/kameleoon/data/custom_data.py +0 -58
- kameleoon-client-python-3.14.0/kameleoon/targeting/models.py +0 -29
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/MANIFEST.in +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/README.md +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/__init__.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/client_readiness/__init__.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/client_readiness/async_readiness.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/client_readiness/threading_readiness.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/configuration/__init__.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/configuration/experiment.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/configuration/feature_flag.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/configuration/me_group.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/configuration/rule.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/configuration/rule_type.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/configuration/settings.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/configuration/variable.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/configuration/variation.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/configuration/variation_by_exposition.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/data/__init__.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/data/browser.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/data/cbscores.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/data/conversion.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/data/cookie.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/data/device.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/data/geolocation.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/data/kcs_heat.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/data/manager/__init__.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/data/manager/forced_experiment_variation.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/data/manager/forced_feature_variation.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/data/manager/forced_variation.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/data/manager/page_view_visit.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/data/manager/visitor_slot.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/data/operating_system.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/data/page_view.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/data/personalization.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/data/unique_identifier.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/data/user_agent.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/data/visitor_visits.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/defaults.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/exceptions.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/helpers/__init__.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/helpers/domain.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/helpers/functions.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/helpers/hasher.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/helpers/logger.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/helpers/multi_threading.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/helpers/nonce.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/helpers/repeat_timer.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/helpers/scheduler.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/helpers/string_utils.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/helpers/visitor_code.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/hybrid/__init__.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/hybrid/hybrid_manager.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/hybrid/hybrid_manager_impl.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/kameleoon_client_config.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/kameleoon_client_factory.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/logging/__init__.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/logging/kameleoon_logger.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/logging/log_level.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/logging/logger.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/managers/__init__.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/managers/asyncexec/__init__.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/managers/asyncexec/async_executor.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/managers/data/__init__.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/managers/data/data_manager.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/managers/evaluation/__init__.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/managers/evaluation/evaluated_experiment.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/managers/remote_data/__init__.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/managers/remote_data/remote_data_manager.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/managers/tracking/__init__.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/managers/tracking/tracking_builder.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/managers/tracking/tracking_manager.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/managers/tracking/visitor_tracking_registry.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/managers/warehouse/__init__.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/managers/warehouse/warehouse_manager.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/network/__init__.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/network/access_token_source.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/network/access_token_source_factory.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/network/activity_event.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/network/cookie/__init__.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/network/cookie/cookie_manager.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/network/net_provider.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/network/net_provider_impl.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/network/network_manager.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/network/network_manager_factory.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/network/network_manager_factory_impl.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/network/network_manager_impl.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/network/query_encodable.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/network/sendable.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/network/service_initialize_context.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/network/service_initialize_context_impl.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/network/services/__init__.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/network/services/automation_service.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/network/services/automation_service_impl.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/network/services/configuration_service.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/network/services/configuration_service_impl.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/network/services/data_service.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/network/services/data_service_impl.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/network/services/service.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/network/services/service_impl.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/network/uri_helper.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/network/url_provider.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/real_time/__init__.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/real_time/real_time_configuration_service.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/real_time/real_time_event.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/storage/__init__.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/storage/cache.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/storage/cache_factory.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/storage/cache_factory_impl.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/storage/cache_impl.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/__init__.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/conditions/__init__.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/conditions/browser_condition.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/conditions/constants.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/conditions/conversion_condition.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/conditions/cookie_condition.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/conditions/custom_datum.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/conditions/device_condition.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/conditions/geolocation_condition.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/conditions/kcs_heat_range_condition.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/conditions/number_condition.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/conditions/operating_system_condition.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/conditions/page_title_condition.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/conditions/page_url_condition.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/conditions/page_view_number_condition.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/conditions/previous_page_condition.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/conditions/sdk_language_condition.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/conditions/string_value_condition.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/conditions/target_experiment_condition.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/conditions/target_feature_flag_condition.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/conditions/target_personalization_condition.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/conditions/targeting_condition.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/conditions/time_elapsed_since_visit_condition.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/conditions/unknown_condition.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/conditions/visit_number_today_condition.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/conditions/visit_number_total_condition.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/conditions/visitor_code_condition.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/conditions/visitor_new_return_condition.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/tree_builder.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/targeting/tree_condition_factory.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/types/__init__.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/types/remote_visitor_data_filter.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/types/variable.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon/types/variation.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon_client_python.egg-info/dependency_links.txt +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon_client_python.egg-info/requires.txt +16 -16
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/kameleoon_client_python.egg-info/top_level.txt +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/setup.cfg +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/setup.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/tests/integration/proj/__init__.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/tests/integration/proj/kameleoon_app/__init__.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/tests/integration/proj/kameleoon_app/admin.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/tests/integration/proj/kameleoon_app/apps.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/tests/integration/proj/kameleoon_app/migrations/__init__.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/tests/integration/proj/kameleoon_app/models.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/tests/integration/proj/kameleoon_app/tests.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/tests/integration/proj/kameleoon_app/views.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/tests/integration/proj/manage.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/tests/integration/proj/proj/__init__.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/tests/integration/proj/proj/asgi.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/tests/integration/proj/proj/settings.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/tests/integration/proj/proj/urls.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/tests/integration/proj/proj/wsgi.py +0 -0
- {kameleoon-client-python-3.14.0 → kameleoon-client-python-3.16.0}/tests/resources/config.yml +0 -0
|
@@ -18,6 +18,7 @@ class CustomDataInfo:
|
|
|
18
18
|
self.__local_only = set()
|
|
19
19
|
self.__visitor_scope = set()
|
|
20
20
|
self.__custom_data_index_by_id = {}
|
|
21
|
+
self.__custom_data_index_by_name = {}
|
|
21
22
|
if custom_data is not None:
|
|
22
23
|
for data in custom_data:
|
|
23
24
|
index = data.get("index")
|
|
@@ -29,6 +30,8 @@ class CustomDataInfo:
|
|
|
29
30
|
self.__visitor_scope.add(index)
|
|
30
31
|
if (id_ := data.get("id")) is not None:
|
|
31
32
|
self.__custom_data_index_by_id[id_] = index
|
|
33
|
+
if (name := data.get("name")) is not None:
|
|
34
|
+
self.__custom_data_index_by_name[name] = index
|
|
32
35
|
if data.get("isMappingIdentifier"):
|
|
33
36
|
if bool(self.__mapping_identifier_index is not None):
|
|
34
37
|
KameleoonLogger.warning(
|
|
@@ -55,5 +58,9 @@ class CustomDataInfo:
|
|
|
55
58
|
return self.__mapping_identifier_index
|
|
56
59
|
|
|
57
60
|
def get_custom_data_index_by_id(self, custom_data_id: int) -> Optional[int]:
|
|
58
|
-
"""Get corresponding custom data
|
|
61
|
+
"""Get corresponding custom data index by custom data id"""
|
|
59
62
|
return self.__custom_data_index_by_id.get(custom_data_id)
|
|
63
|
+
|
|
64
|
+
def get_custom_data_index_by_name(self, custom_data_name: str) -> Optional[int]:
|
|
65
|
+
"""Get corresponding custom data index by custom data name"""
|
|
66
|
+
return self.__custom_data_index_by_name.get(custom_data_name)
|
|
@@ -20,7 +20,7 @@ class DataFile:
|
|
|
20
20
|
@staticmethod
|
|
21
21
|
def default(environment: Optional[str]) -> "DataFile":
|
|
22
22
|
"""Creates new instance of `DataFile` initialized with default values"""
|
|
23
|
-
data_file = DataFile(environment, None, Settings(), {}, CustomDataInfo(None))
|
|
23
|
+
data_file = DataFile(environment, None, Settings(), {}, {}, CustomDataInfo(None))
|
|
24
24
|
return data_file
|
|
25
25
|
|
|
26
26
|
@staticmethod
|
|
@@ -33,13 +33,13 @@ class DataFile:
|
|
|
33
33
|
custom_data_info = CustomDataInfo(configuration.get("customData"))
|
|
34
34
|
feature_flags = DataFile.__parse_feature_flags(configuration, segments, custom_data_info)
|
|
35
35
|
holdout = Experiment.from_json(configuration["holdout"]) if "holdout" in configuration else None
|
|
36
|
-
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)
|
|
37
37
|
return data_file
|
|
38
38
|
|
|
39
39
|
@staticmethod
|
|
40
40
|
def __parse_segments(configuration: Dict[str, Any]) -> Dict[int, Segment]:
|
|
41
41
|
return {
|
|
42
|
-
(seg := Segment(jobj)).id_: seg
|
|
42
|
+
(seg := Segment.from_json(jobj)).id_: seg
|
|
43
43
|
for jobj in configuration.get("segments") or []
|
|
44
44
|
}
|
|
45
45
|
|
|
@@ -58,18 +58,21 @@ class DataFile:
|
|
|
58
58
|
environment: Optional[str],
|
|
59
59
|
last_modified: Optional[str],
|
|
60
60
|
settings: Settings,
|
|
61
|
+
segments: Dict[int, Segment],
|
|
61
62
|
feature_flags: Dict[str, FeatureFlag],
|
|
62
63
|
custom_data_info: CustomDataInfo,
|
|
63
64
|
holdout: Optional[Experiment] = None,
|
|
64
65
|
) -> None:
|
|
65
66
|
KameleoonLogger.debug(
|
|
66
|
-
"CALL: DataFile(environment: %s, last_modified: %s, settings: %s, feature_flags
|
|
67
|
+
"CALL: DataFile(environment: %s, last_modified: %s, settings: %s, segments, feature_flags,"
|
|
67
68
|
" custom_data_info: %s, holdout: %s)",
|
|
68
|
-
environment, last_modified, settings,
|
|
69
|
+
environment, last_modified, settings, custom_data_info, holdout
|
|
69
70
|
)
|
|
70
71
|
self.__environment = environment # pylint: disable=W0238
|
|
71
72
|
self.__last_modified = last_modified
|
|
72
73
|
self.__settings = settings
|
|
74
|
+
self.__segments = segments
|
|
75
|
+
self.__audience_tracking_segments = [seg for seg in segments.values() if seg.audience_tracking]
|
|
73
76
|
self.__feature_flags: Dict[str, FeatureFlag] = feature_flags
|
|
74
77
|
self.__me_groups = self.__make_me_groups(feature_flags.values())
|
|
75
78
|
self.__has_any_targeted_delivery_rule = any(
|
|
@@ -79,7 +82,6 @@ class DataFile:
|
|
|
79
82
|
for rule in ff.rules
|
|
80
83
|
)
|
|
81
84
|
self.__variation_by_id: Dict[int, VariationByExposition] = {}
|
|
82
|
-
self.__rule_by_segment_id: Dict[int, Rule] = {}
|
|
83
85
|
self.__rule_info_by_exp_id: Dict[int, DataFile.RuleInfo] = {}
|
|
84
86
|
self.__feature_flag_by_id: Dict[int, FeatureFlag] = {}
|
|
85
87
|
self.__experiment_ids_with_js_css_variable: Set[int] = set()
|
|
@@ -87,9 +89,9 @@ class DataFile:
|
|
|
87
89
|
self.__custom_data_info = custom_data_info
|
|
88
90
|
self.__holdout = holdout
|
|
89
91
|
KameleoonLogger.debug(
|
|
90
|
-
"RETURN: DataFile(environment: %s, last_modified: %s, settings: %s, feature_flags
|
|
92
|
+
"RETURN: DataFile(environment: %s, last_modified: %s, settings: %s, segments, feature_flags,"
|
|
91
93
|
" custom_data_info: %s, holdout: %s)",
|
|
92
|
-
environment, last_modified, settings,
|
|
94
|
+
environment, last_modified, settings, custom_data_info, holdout
|
|
93
95
|
)
|
|
94
96
|
|
|
95
97
|
@property
|
|
@@ -102,6 +104,16 @@ class DataFile:
|
|
|
102
104
|
"""Returns settings"""
|
|
103
105
|
return self.__settings
|
|
104
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
|
+
|
|
105
117
|
@property
|
|
106
118
|
def feature_flags(self) -> Dict[str, FeatureFlag]:
|
|
107
119
|
"""Returns dictionary of all feature flags stored by feature keys"""
|
|
@@ -117,11 +129,6 @@ class DataFile:
|
|
|
117
129
|
"""Returns dictionary of all feature flags stored by id"""
|
|
118
130
|
return self.__feature_flag_by_id
|
|
119
131
|
|
|
120
|
-
@property
|
|
121
|
-
def rule_by_segment_id(self) -> Dict[int, Rule]:
|
|
122
|
-
"""Returns dictionary of all rule stored by segment id"""
|
|
123
|
-
return self.__rule_by_segment_id
|
|
124
|
-
|
|
125
132
|
@property
|
|
126
133
|
def rule_info_by_exp_id(self) -> Dict[int, "DataFile.RuleInfo"]:
|
|
127
134
|
"""Returns dictionary of all rule related info stored by experiment id"""
|
|
@@ -169,7 +176,6 @@ class DataFile:
|
|
|
169
176
|
if feature_flag.rules is not None:
|
|
170
177
|
has_feature_flag_variable_js_css = self.__has_feature_flag_variable_js_css(feature_flag)
|
|
171
178
|
for rule in feature_flag.rules:
|
|
172
|
-
self.__rule_by_segment_id[rule.segment_id] = rule
|
|
173
179
|
self.__rule_info_by_exp_id[rule.experiment.id_] = DataFile.RuleInfo(feature_flag, rule)
|
|
174
180
|
if has_feature_flag_variable_js_css:
|
|
175
181
|
self.__experiment_ids_with_js_css_variable.add(rule.experiment.id_)
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""Custom data"""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import Optional, Tuple, Union
|
|
5
|
+
from kameleoon.data.data import Data, DataType
|
|
6
|
+
from kameleoon.network.sendable import DuplicationUnsafeSendableBase
|
|
7
|
+
from kameleoon.network.query_builder import QueryBuilder, QueryParam, QueryParams
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CustomData(Data, DuplicationUnsafeSendableBase):
|
|
11
|
+
"""Custom data"""
|
|
12
|
+
|
|
13
|
+
EVENT_TYPE = "customData"
|
|
14
|
+
|
|
15
|
+
def __init__(self, index_or_name: Union[int, str], *args: str, overwrite=True) -> None:
|
|
16
|
+
"""
|
|
17
|
+
:param index_or_name: Index or Name of the custom data. This field is mandatory.
|
|
18
|
+
:type index_or_name: Union[int, str]
|
|
19
|
+
:param args: Values of the custom data to be stored. This field is mandatory.
|
|
20
|
+
:type args: *str
|
|
21
|
+
:param overwrite: Flag to explicitly control how the values are stored and how they appear in reports.
|
|
22
|
+
This field is optional, `True` by default.
|
|
23
|
+
:type overwrite: bool
|
|
24
|
+
"""
|
|
25
|
+
# pylint: disable=invalid-name,redefined-builtin
|
|
26
|
+
super().__init__()
|
|
27
|
+
# pylint: disable=W0511
|
|
28
|
+
if isinstance(index_or_name, int):
|
|
29
|
+
self.__index = index_or_name
|
|
30
|
+
self._name = None
|
|
31
|
+
else:
|
|
32
|
+
self.__index = -1
|
|
33
|
+
self._name = index_or_name
|
|
34
|
+
self.__values = args
|
|
35
|
+
self.__overwrite = overwrite
|
|
36
|
+
|
|
37
|
+
def _named_to_indexed(self, index: int) -> "CustomData":
|
|
38
|
+
"""Makes an indexed Custom Data instance based on the current instance"""
|
|
39
|
+
custom_data = CustomData(index, *self.__values, overwrite=self.__overwrite)
|
|
40
|
+
custom_data._name = self._name
|
|
41
|
+
return custom_data
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def id(self) -> int: # pylint: disable=C0103
|
|
45
|
+
"""
|
|
46
|
+
Deprecated. Please use `index` instead.
|
|
47
|
+
|
|
48
|
+
Returns index
|
|
49
|
+
"""
|
|
50
|
+
return self.__index
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def index(self) -> int:
|
|
54
|
+
"""Returns index"""
|
|
55
|
+
return self.__index
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def name(self) -> Optional[str]:
|
|
59
|
+
"""Returns name"""
|
|
60
|
+
return self._name
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def values(self) -> Tuple[str, ...]:
|
|
64
|
+
"""Returns values"""
|
|
65
|
+
return self.__values
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def overwrite(self) -> bool:
|
|
69
|
+
"""Returns overwrite"""
|
|
70
|
+
return self.__overwrite
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def data_type(self) -> DataType:
|
|
74
|
+
return DataType.CUSTOM_DATA
|
|
75
|
+
|
|
76
|
+
def _add_query_params(self, query_builder: QueryBuilder) -> None:
|
|
77
|
+
str_values = json.dumps({v: 1 for v in self.__values}, separators=(",", ":"))
|
|
78
|
+
query_builder.extend(
|
|
79
|
+
QueryParam(QueryParams.EVENT_TYPE, self.EVENT_TYPE, False),
|
|
80
|
+
QueryParam(QueryParams.INDEX, str(self.id), False),
|
|
81
|
+
QueryParam(QueryParams.VALUES_COUNT_MAP, str_values),
|
|
82
|
+
QueryParam(QueryParams.OVERWRITE, "true" if self.__overwrite else "false", False),
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
def __str__(self):
|
|
86
|
+
return (
|
|
87
|
+
"CustomData{"
|
|
88
|
+
f"index:{self.__index},name:'{self._name}',values:{self.__values},overwrite:{self.__overwrite}"
|
|
89
|
+
"}"
|
|
90
|
+
)
|
|
@@ -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.14.0 → kameleoon-client-python-3.16.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.
|
|
@@ -219,6 +227,8 @@ class Visitor: # pylint: disable=R0904
|
|
|
219
227
|
KameleoonLogger.debug('CALL: Visitor.add_data(args: %s, overwrite: %s)', args, overwrite)
|
|
220
228
|
with self._data.lock:
|
|
221
229
|
for data in args:
|
|
230
|
+
if data is None:
|
|
231
|
+
continue
|
|
222
232
|
data_type = type(data)
|
|
223
233
|
data_adder = self.DATA_ADDERS.get(data_type)
|
|
224
234
|
if data_adder:
|
|
@@ -283,6 +293,7 @@ class Visitor: # pylint: disable=R0904
|
|
|
283
293
|
self.conversions: Optional[List[Conversion]] = None
|
|
284
294
|
self.variations: Optional[Dict[int, AssignedVariation]] = None
|
|
285
295
|
self.personalizations: Optional[Dict[int, Personalization]] = None
|
|
296
|
+
self.targeted_segments: Optional[Dict[int, TargetedSegment]] = None
|
|
286
297
|
self.forced_variations: Optional[Dict[int, ForcedExperimentVariation]] = None
|
|
287
298
|
self.simulated_variations: Optional[Dict[str, ForcedFeatureVariation]] = None
|
|
288
299
|
self.kcs_heat: Optional[KcsHeat] = None
|
|
@@ -309,6 +320,8 @@ class Visitor: # pylint: disable=R0904
|
|
|
309
320
|
yield from (visit.page_view for visit in list(self.page_view_visits.values()))
|
|
310
321
|
if self.variations is not None:
|
|
311
322
|
yield from list(self.variations.values())
|
|
323
|
+
if self.targeted_segments is not None:
|
|
324
|
+
yield from list(self.targeted_segments.values())
|
|
312
325
|
if self.conversions is not None:
|
|
313
326
|
yield from list(self.conversions)
|
|
314
327
|
|
|
@@ -331,6 +344,8 @@ class Visitor: # pylint: disable=R0904
|
|
|
331
344
|
count += len(self.page_view_visits)
|
|
332
345
|
if self.variations is not None:
|
|
333
346
|
count += len(self.variations)
|
|
347
|
+
if self.targeted_segments is not None:
|
|
348
|
+
count += len(self.targeted_segments)
|
|
334
349
|
if self.conversions is not None:
|
|
335
350
|
count += len(self.conversions)
|
|
336
351
|
return count
|
|
@@ -374,6 +389,12 @@ class Visitor: # pylint: disable=R0904
|
|
|
374
389
|
if context.overwrite or personalization.id not in self._data.personalizations:
|
|
375
390
|
self._data.personalizations[personalization.id] = personalization
|
|
376
391
|
|
|
392
|
+
def _add_targeted_segment(self, context: DataAddingContext) -> None:
|
|
393
|
+
targeted_segment = cast(TargetedSegment, context.data)
|
|
394
|
+
if self._data.targeted_segments is None:
|
|
395
|
+
self._data.targeted_segments = {}
|
|
396
|
+
self._data.targeted_segments[targeted_segment.id] = targeted_segment
|
|
397
|
+
|
|
377
398
|
def _add_forced_feature_variation(self, context: DataAddingContext) -> None:
|
|
378
399
|
ffv = cast(ForcedFeatureVariation, context.data)
|
|
379
400
|
if self._data.simulated_variations is None:
|
|
@@ -405,8 +426,8 @@ class Visitor: # pylint: disable=R0904
|
|
|
405
426
|
custom_data = cast(CustomData, context.data)
|
|
406
427
|
if self._data.custom_data_dict is None:
|
|
407
428
|
self._data.custom_data_dict = {}
|
|
408
|
-
if context.overwrite or custom_data.
|
|
409
|
-
self._data.custom_data_dict[custom_data.
|
|
429
|
+
if context.overwrite or custom_data.index not in self._data.custom_data_dict:
|
|
430
|
+
self._data.custom_data_dict[custom_data.index] = custom_data
|
|
410
431
|
|
|
411
432
|
def _add_page_view(self, context: DataAddingContext) -> None:
|
|
412
433
|
page_view = cast(PageView, context.data)
|
|
@@ -471,6 +492,7 @@ class Visitor: # pylint: disable=R0904
|
|
|
471
492
|
VisitorVisits: _set_visitor_visits,
|
|
472
493
|
AssignedVariation: _add_variation,
|
|
473
494
|
Personalization: _add_personalization,
|
|
495
|
+
TargetedSegment: _add_targeted_segment,
|
|
474
496
|
ForcedFeatureVariation: _add_forced_feature_variation,
|
|
475
497
|
ForcedExperimentVariation: _add_forced_experiment_variation,
|
|
476
498
|
UniqueIdentifier: _set_is_unique_identifier,
|
|
@@ -87,15 +87,20 @@ class VisitorManager:
|
|
|
87
87
|
|
|
88
88
|
def __process_custom_data(
|
|
89
89
|
self, visitor_code: str, visitor: Visitor, custom_data_info: CustomDataInfo, custom_data: CustomData
|
|
90
|
-
) -> CustomData:
|
|
90
|
+
) -> Optional[CustomData]:
|
|
91
91
|
"""Processes custom data, potentially modifying or mapping it based on certain conditions."""
|
|
92
|
+
if custom_data.name is not None:
|
|
93
|
+
cd_index = custom_data_info.get_custom_data_index_by_name(custom_data.name)
|
|
94
|
+
if cd_index is None:
|
|
95
|
+
return None
|
|
96
|
+
custom_data = custom_data._named_to_indexed(cd_index)
|
|
92
97
|
# We shouldn't send custom data with local only type
|
|
93
|
-
if custom_data_info.is_local_only(custom_data.
|
|
98
|
+
if custom_data_info.is_local_only(custom_data.index):
|
|
94
99
|
custom_data.mark_as_sent()
|
|
95
100
|
# If mappingIdentifier is passed, we should link anonymous visitor with real unique userId.
|
|
96
101
|
# After authorization, customer must be able to continue work with userId, but hash for variation
|
|
97
102
|
# should be calculated based on anonymous visitor code, that's why set MappingIdentifier to visitor.
|
|
98
|
-
if custom_data_info.is_mapping_identifier(custom_data.
|
|
103
|
+
if custom_data_info.is_mapping_identifier(custom_data.index) and custom_data.values and custom_data.values[0]:
|
|
99
104
|
visitor.mapping_identifier = visitor_code
|
|
100
105
|
user_id = custom_data.values[0]
|
|
101
106
|
if visitor_code != user_id:
|
|
@@ -6,7 +6,8 @@ from kameleoon.network.query_builder import QueryBuilder, QueryParam, QueryParam
|
|
|
6
6
|
class MappingIdentifier(CustomData):
|
|
7
7
|
"""Mapping identifier"""
|
|
8
8
|
def __init__(self, custom_data: CustomData) -> None:
|
|
9
|
-
super().__init__(custom_data.
|
|
9
|
+
super().__init__(custom_data.index, *custom_data.values, overwrite=custom_data.overwrite)
|
|
10
|
+
self._name = custom_data.name
|
|
10
11
|
|
|
11
12
|
@property
|
|
12
13
|
def unsent(self) -> bool:
|
|
@@ -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.14.0 → kameleoon-client-python-3.16.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,
|
|
@@ -1380,7 +1381,7 @@ class KameleoonClient:
|
|
|
1380
1381
|
campaign_id,
|
|
1381
1382
|
rule,
|
|
1382
1383
|
)
|
|
1383
|
-
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)
|
|
1384
1385
|
KameleoonLogger.debug(
|
|
1385
1386
|
"RETURN: KameleoonClient.__check_targeting(visitor_code: %s, campaign_id: %s, rule: %s) -> "
|
|
1386
1387
|
"(targeting: %s)",
|
|
@@ -1782,3 +1783,25 @@ class KameleoonClient:
|
|
|
1782
1783
|
variation_key,
|
|
1783
1784
|
force_targeting,
|
|
1784
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)
|
|
@@ -103,7 +103,7 @@ class RemoteVisitorData:
|
|
|
103
103
|
"""Marks data as sent based on the provided `custom_data_info`."""
|
|
104
104
|
if self.__custom_data_dict:
|
|
105
105
|
for data in self.__custom_data_dict.values():
|
|
106
|
-
if custom_data_info is None or not custom_data_info.is_visitor_scope(data.
|
|
106
|
+
if custom_data_info is None or not custom_data_info.is_visitor_scope(data.index):
|
|
107
107
|
data.mark_as_sent()
|
|
108
108
|
if self.__experiments:
|
|
109
109
|
for experiment in self.__experiments.values():
|
|
@@ -32,7 +32,6 @@ class KameleoonWSGIMiddleware:
|
|
|
32
32
|
environ["HTTP_COOKIE"] = f"{VISITOR_CODE_COOKIE}={visitor_code}"
|
|
33
33
|
|
|
34
34
|
def _custom_start_response(status, headers, exc_info=None):
|
|
35
|
-
# pylint: disable=W0212
|
|
36
35
|
self._client._cookie_manager.update(visitor_code, self._client._is_consent_given(visitor_code), cookies)
|
|
37
36
|
headers.append(("Set-Cookie", cookies.output(header="")))
|
|
38
37
|
return start_response(status, headers, exc_info)
|
|
@@ -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}}}"
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"""Targeting Segment"""
|
|
2
2
|
|
|
3
3
|
from typing import Optional
|
|
4
|
-
from kameleoon.configuration.rule import Rule
|
|
5
4
|
from kameleoon.data.manager.visitor import Visitor
|
|
6
5
|
from kameleoon.data.manager.visitor_manager import VisitorManager
|
|
7
6
|
from kameleoon.helpers.functions import enum_from_literal
|
|
@@ -16,6 +15,7 @@ from kameleoon.targeting.conditions.target_experiment_condition import TargetExp
|
|
|
16
15
|
from kameleoon.targeting.conditions.target_personalization_condition import TargetPersonalizationCondition
|
|
17
16
|
from kameleoon.targeting.conditions.targeting_condition import TargetingConditionType
|
|
18
17
|
from kameleoon.targeting.conditions.visit_number_today_condition import VisitNumberTodayCondition
|
|
18
|
+
from kameleoon.targeting.models import Segment
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
class TargetingManager:
|
|
@@ -26,22 +26,23 @@ class TargetingManager:
|
|
|
26
26
|
self.__visitor_manager = visitor_manager
|
|
27
27
|
|
|
28
28
|
# pylint: disable=W0238
|
|
29
|
-
def check_targeting(self, visitor_code: str, campaign_id: int,
|
|
29
|
+
def check_targeting(self, visitor_code: str, campaign_id: Optional[int], segment: Optional[Segment]):
|
|
30
30
|
"""Check if visitor is targeted for rule"""
|
|
31
31
|
KameleoonLogger.debug(
|
|
32
|
-
"CALL: TargetingManager.check_targeting(visitor_code: %s, campaign_id: %s,
|
|
33
|
-
visitor_code, campaign_id,
|
|
32
|
+
"CALL: TargetingManager.check_targeting(visitor_code: %s, campaign_id: %s, segment: %s)",
|
|
33
|
+
visitor_code, campaign_id, segment
|
|
34
34
|
)
|
|
35
|
-
segment = rule.targeting_segment
|
|
36
35
|
if segment is None:
|
|
37
36
|
targeting: Optional[bool] = True
|
|
38
37
|
else:
|
|
39
38
|
visitor = self.__visitor_manager.get_visitor(visitor_code)
|
|
40
|
-
targeting = segment.check_tree(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
targeting = segment.check_tree(
|
|
40
|
+
lambda type: self.__get_condition_data(type, visitor, visitor_code, campaign_id)
|
|
41
|
+
)
|
|
42
|
+
KameleoonLogger.debug(
|
|
43
|
+
"RETURN: TargetingManager.check_targeting(visitor_code: %s, campaign_id: %s, "
|
|
44
|
+
"segment: %s) -> (targeting: %s)", visitor_code, campaign_id, segment, targeting
|
|
45
|
+
)
|
|
45
46
|
return targeting
|
|
46
47
|
|
|
47
48
|
# pylint: disable=R0911
|
|
@@ -50,9 +51,9 @@ class TargetingManager:
|
|
|
50
51
|
condition_type_literal: str,
|
|
51
52
|
visitor: Optional[Visitor],
|
|
52
53
|
visitor_code: str,
|
|
53
|
-
campaign_id: int,
|
|
54
|
+
campaign_id: Optional[int],
|
|
54
55
|
):
|
|
55
|
-
# pylint: disable=E1101
|
|
56
|
+
# pylint: disable=E1101
|
|
56
57
|
KameleoonLogger.debug(
|
|
57
58
|
"CALL: TargetingManager.__get_condition_data(condition_type_literal: %s, visitor, visitor_code: %s, "
|
|
58
59
|
"campaign_id: %s)", condition_type_literal, visitor_code, campaign_id
|
|
@@ -72,9 +73,10 @@ class TargetingManager:
|
|
|
72
73
|
lambda type: self.__get_condition_data(type, visitor, visitor_code, campaign_id),
|
|
73
74
|
)
|
|
74
75
|
elif condition_type == TargetingConditionType.EXCLUSIVE_EXPERIMENT:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
if campaign_id is not None:
|
|
77
|
+
condition_data = ExclusiveExperimentCondition.TargetingData(
|
|
78
|
+
campaign_id, visitor.variations if visitor else {}, visitor.personalizations if visitor else {}
|
|
79
|
+
)
|
|
78
80
|
elif condition_type == TargetingConditionType.VISITOR_CODE:
|
|
79
81
|
condition_data = visitor_code
|
|
80
82
|
elif condition_type == TargetingConditionType.SDK_LANGUAGE:
|
|
@@ -39,6 +39,7 @@ kameleoon/data/mapping_identifier.py
|
|
|
39
39
|
kameleoon/data/operating_system.py
|
|
40
40
|
kameleoon/data/page_view.py
|
|
41
41
|
kameleoon/data/personalization.py
|
|
42
|
+
kameleoon/data/targeted_segment.py
|
|
42
43
|
kameleoon/data/unique_identifier.py
|
|
43
44
|
kameleoon/data/user_agent.py
|
|
44
45
|
kameleoon/data/visitor_visits.py
|