wingify-fme-python-sdk 1.50.0__tar.gz → 1.60.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.
- {wingify_fme_python_sdk-1.50.0/wingify_fme_python_sdk.egg-info → wingify_fme_python_sdk-1.60.0}/PKG-INFO +22 -1
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/README.md +21 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/setup.py +2 -2
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/api/get_flag_api.py +61 -1
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/constants/Constants.py +1 -1
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/enums/event_enum.py +1 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/settings/settings_model.py +8 -1
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/user/context_model.py +16 -2
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/segmentation_evaluator/core/segmentation_manager.py +11 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/segmentation_evaluator/enums/segment_operator_value_enum.py +1 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/segmentation_evaluator/evaluators/segment_evaluator.py +4 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/segmentation_evaluator/evaluators/segment_operand_evaluator.py +24 -0
- wingify_fme_python_sdk-1.60.0/wingify/packages/segmentation_evaluator/utils/web_testing_segment_util.py +295 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/resources/debug-messages.json +2 -1
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/resources/error-messages.json +8 -1
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/impression_util.py +69 -1
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0/wingify_fme_python_sdk.egg-info}/PKG-INFO +22 -1
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify_fme_python_sdk.egg-info/SOURCES.txt +1 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/LICENSE +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/MANIFEST.in +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/NOTICE +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/requirements-dev.txt +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/requirements.txt +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/setup.cfg +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/vwo/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/vwo/vwo.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/vwo/vwo_builder.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/vwo/vwo_client.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/api/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/api/set_attribute_api.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/api/track_api.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/constants/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/decorators/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/decorators/storage_decorator.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/enums/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/enums/api_enum.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/enums/campaign_type_enum.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/enums/debug_category_enum.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/enums/headers_enum.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/enums/hooks_enum.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/enums/status_enum.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/enums/storage_enum.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/enums/url_enum.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/Alias.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/AliasSetResponse.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/batch_event_data.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/campaign/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/campaign/campaign_model.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/campaign/feature_model.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/campaign/holdout_model.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/campaign/impact_campaign_model.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/campaign/metric_model.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/campaign/rule_model.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/campaign/variable_model.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/campaign/variation_model.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/gateway_service_model.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/schemas/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/schemas/campaign_group_schema.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/schemas/campaign_metric_schema.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/schemas/campaign_schema.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/schemas/campaign_variation_schema.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/schemas/empty_object_schema.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/schemas/feature_schema.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/schemas/group_schema.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/schemas/holdout_schema.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/schemas/rule_schema.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/schemas/settings_schema.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/schemas/variable_schema.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/settings/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/storage/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/storage/storage_model.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/user/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/user/context_vwo_model.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/user/get_flag.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/vwo_options_model.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/wingify_options_model.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/decision_maker/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/decision_maker/decision_maker.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/logger/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/logger/core/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/logger/core/log_manager.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/logger/core/log_transport_manager.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/logger/enums/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/logger/enums/log_level_enum.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/logger/enums/log_level_number_enum.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/logger/log_message_builder.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/logger/logger.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/logger/transports/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/logger/transports/console_transport.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/network_layer/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/network_layer/client/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/network_layer/client/network_client.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/network_layer/handlers/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/network_layer/handlers/request_handler.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/network_layer/manager/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/network_layer/manager/network_manager.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/network_layer/models/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/network_layer/models/global_request_model.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/network_layer/models/request_model.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/network_layer/models/response_model.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/segmentation_evaluator/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/segmentation_evaluator/core/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/segmentation_evaluator/enums/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/segmentation_evaluator/enums/segment_operand_regex_enum.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/segmentation_evaluator/enums/segment_operand_value_enum.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/segmentation_evaluator/evaluators/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/segmentation_evaluator/utils/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/segmentation_evaluator/utils/segment_util.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/storage/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/storage/connector.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/storage/storage.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/resources/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/resources/info-message.json +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/resources/trace-messages.json +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/resources/warn-messages.json +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/services/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/services/batch_event_queue.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/services/campaign_decision_service.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/services/hooks_manager.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/services/settings_manager.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/services/storage_service.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/services/url_service.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/__init__.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/aliasing_util.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/brand_context.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/brand_util.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/campaign_util.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/data_type_util.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/debugger_service_util.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/decision_util.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/event_util.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/function_util.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/gateway_service_util.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/holdout_util.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/log_message_util.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/meg_util.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/model_utils.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/network_util.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/rule_evaluation_util.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/settings_util.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/usage_stats_util.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/uuid_util.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/vwo_client.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/wingify.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/wingify_builder.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/wingify_client.py +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify_fme_python_sdk.egg-info/dependency_links.txt +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify_fme_python_sdk.egg-info/requires.txt +0 -0
- {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify_fme_python_sdk.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: wingify-fme-python-sdk
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.60.0
|
|
4
4
|
Summary: Wingify Feature Management and Experimentation SDK for Python
|
|
5
5
|
Home-page: https://github.com/wingify/vwo-fme-python-sdk
|
|
6
6
|
Author: Wingify
|
|
@@ -168,9 +168,30 @@ context = {
|
|
|
168
168
|
'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',
|
|
169
169
|
'ip_address': '1.1.1.1',
|
|
170
170
|
'session_id': 1697123456 # Optional: Custom session ID for web client integration
|
|
171
|
+
'platformVariables': {
|
|
172
|
+
# Reference example only:.
|
|
173
|
+
# In production, fetch campaign assignments using script run in frontend and pass that object to backend as webTestingCampaigns.
|
|
174
|
+
'webTestingCampaigns': '{"122":"1","130":"2"}'
|
|
175
|
+
},
|
|
176
|
+
'session_id': 1697123456 # Optional: Custom session ID for web client integration
|
|
171
177
|
}
|
|
172
178
|
```
|
|
173
179
|
|
|
180
|
+
## Web testing pre-segmentation
|
|
181
|
+
|
|
182
|
+
Server-side flag decisions can align with **Web Testing** (browser) experiments. **Campaign assignments are typically read on the frontend** (for example, VWO cookies) and sent to your server; pass them in `platformVariables.webTestingCampaigns` as a map of **campaign ID -> variation ID** (strings), or as a **JSON string** of that object.
|
|
183
|
+
|
|
184
|
+
Pre-segment rules in the VWO dashboard can use the **`campaignVariation`** operator:
|
|
185
|
+
|
|
186
|
+
| Operand pattern | Meaning |
|
|
187
|
+
| --------------- | ------- |
|
|
188
|
+
| `C` | User is in campaign `C` (any variation). |
|
|
189
|
+
| `!C` | User is **not** in campaign `C`. |
|
|
190
|
+
| `C_V` | User is in campaign `C` with variation `V`. |
|
|
191
|
+
| `C_!V` | User is in campaign `C` and assigned variation is **not** `V`. |
|
|
192
|
+
|
|
193
|
+
Rollout rules evaluate pre-segments from the **first variation** only; AB/testing rules use **campaign-level** segments.
|
|
194
|
+
|
|
174
195
|
## Session Management
|
|
175
196
|
|
|
176
197
|
The SDK provides automatic session management capabilities to enable seamless integration with VWO's web client testing campaigns. Session IDs are automatically generated and managed to connect server-side feature flag decisions with client-side user sessions.
|
|
@@ -141,9 +141,30 @@ context = {
|
|
|
141
141
|
'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',
|
|
142
142
|
'ip_address': '1.1.1.1',
|
|
143
143
|
'session_id': 1697123456 # Optional: Custom session ID for web client integration
|
|
144
|
+
'platformVariables': {
|
|
145
|
+
# Reference example only:.
|
|
146
|
+
# In production, fetch campaign assignments using script run in frontend and pass that object to backend as webTestingCampaigns.
|
|
147
|
+
'webTestingCampaigns': '{"122":"1","130":"2"}'
|
|
148
|
+
},
|
|
149
|
+
'session_id': 1697123456 # Optional: Custom session ID for web client integration
|
|
144
150
|
}
|
|
145
151
|
```
|
|
146
152
|
|
|
153
|
+
## Web testing pre-segmentation
|
|
154
|
+
|
|
155
|
+
Server-side flag decisions can align with **Web Testing** (browser) experiments. **Campaign assignments are typically read on the frontend** (for example, VWO cookies) and sent to your server; pass them in `platformVariables.webTestingCampaigns` as a map of **campaign ID -> variation ID** (strings), or as a **JSON string** of that object.
|
|
156
|
+
|
|
157
|
+
Pre-segment rules in the VWO dashboard can use the **`campaignVariation`** operator:
|
|
158
|
+
|
|
159
|
+
| Operand pattern | Meaning |
|
|
160
|
+
| --------------- | ------- |
|
|
161
|
+
| `C` | User is in campaign `C` (any variation). |
|
|
162
|
+
| `!C` | User is **not** in campaign `C`. |
|
|
163
|
+
| `C_V` | User is in campaign `C` with variation `V`. |
|
|
164
|
+
| `C_!V` | User is in campaign `C` and assigned variation is **not** `V`. |
|
|
165
|
+
|
|
166
|
+
Rollout rules evaluate pre-segments from the **first variation** only; AB/testing rules use **campaign-level** segments.
|
|
167
|
+
|
|
147
168
|
## Session Management
|
|
148
169
|
|
|
149
170
|
The SDK provides automatic session management capabilities to enable seamless integration with VWO's web client testing campaigns. Session IDs are automatically generated and managed to connect server-side feature flag decisions with client-side user sessions.
|
|
@@ -46,7 +46,7 @@ _BRAND_METADATA = {
|
|
|
46
46
|
},
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
_brand = _BRAND_METADATA.get(SDK_BRAND, _BRAND_METADATA["
|
|
49
|
+
_brand = _BRAND_METADATA.get(SDK_BRAND, _BRAND_METADATA["wingify"])
|
|
50
50
|
|
|
51
51
|
_all_packages = find_packages(exclude=["tests"])
|
|
52
52
|
# Both wheels ship wingify (core) + vwo (legacy facade). Customers import one or the other.
|
|
@@ -143,7 +143,7 @@ class PostDevelopCommand(develop):
|
|
|
143
143
|
|
|
144
144
|
setup(
|
|
145
145
|
name=_brand["name"],
|
|
146
|
-
version="1.
|
|
146
|
+
version="1.60.0",
|
|
147
147
|
description=_brand["description"],
|
|
148
148
|
long_description=long_description,
|
|
149
149
|
long_description_content_type="text/markdown",
|
|
@@ -36,6 +36,7 @@ from ..utils.decision_util import evaluate_traffic_and_get_variation
|
|
|
36
36
|
from ..utils.impression_util import (
|
|
37
37
|
send_impression_for_variation_shown_batch,
|
|
38
38
|
send_impression_for_variation_shown,
|
|
39
|
+
create_and_send_impression_for_usage_tracking,
|
|
39
40
|
)
|
|
40
41
|
from ..models.user.get_flag import GetFlag
|
|
41
42
|
from ..services.storage_service import StorageService
|
|
@@ -76,6 +77,9 @@ class GetFlagApi:
|
|
|
76
77
|
:param hook_manager: The hook manager to execute hooks.
|
|
77
78
|
"""
|
|
78
79
|
|
|
80
|
+
# Flag for usage tracking - false if no variation shown call is sent
|
|
81
|
+
is_variation_shown_fired = False
|
|
82
|
+
|
|
79
83
|
feature = get_feature_from_key(settings, feature_key)
|
|
80
84
|
is_enabled = False
|
|
81
85
|
variables = []
|
|
@@ -173,6 +177,15 @@ class GetFlagApi:
|
|
|
173
177
|
)
|
|
174
178
|
else:
|
|
175
179
|
send_impression_for_variation_shown_batch(holdout_payloads, account_id=settings.get_account_id(), sdk_key=settings.get_sdk_key())
|
|
180
|
+
|
|
181
|
+
# if impression is fired for any of the holdouts, set is_variation_shown_fired to True
|
|
182
|
+
if holdout_payloads and len(holdout_payloads) > 0:
|
|
183
|
+
is_variation_shown_fired = True
|
|
184
|
+
|
|
185
|
+
# Case: User is already part of a holdout (stored decision).
|
|
186
|
+
if settings.get_is_tracking_usage_enabled() and not is_variation_shown_fired:
|
|
187
|
+
create_and_send_impression_for_usage_tracking(settings, feature_key, context)
|
|
188
|
+
|
|
176
189
|
# Return a disabled flag as user is in persistent holdout
|
|
177
190
|
return GetFlag(is_enabled=False, variables=[], session_id=context.get_session_id(), uuid=context.get_vwo_uuid())
|
|
178
191
|
if stored_data and stored_data.get("experimentVariationId"):
|
|
@@ -194,7 +207,19 @@ class GetFlagApi:
|
|
|
194
207
|
)
|
|
195
208
|
decision["isUserPartOfCampaign"] = True
|
|
196
209
|
# send network calls for holdouts that are newly added in settings and are not present in storage
|
|
197
|
-
|
|
210
|
+
# Calculate the initial size of not_in_holdout_id
|
|
211
|
+
initial_size = len(stored_data.get("notInHoldoutId") or []) if stored_data else 0
|
|
212
|
+
# send network calls for not_in_holdout_id
|
|
213
|
+
updated_not_in_holdouts = send_network_calls_for_not_in_holdouts(settings, feature, context, decision, stored_data, storage_service)
|
|
214
|
+
# if variationShown event is fired, set is_variation_shown_fired to True - Optimization for newly added holdouts
|
|
215
|
+
if updated_not_in_holdouts and len(updated_not_in_holdouts) > initial_size:
|
|
216
|
+
is_variation_shown_fired = True
|
|
217
|
+
|
|
218
|
+
# Case: Feature found in storage
|
|
219
|
+
# Send usage tracking for cached experiment decision if usage tracking is enabled
|
|
220
|
+
if settings.get_is_tracking_usage_enabled() and not is_variation_shown_fired:
|
|
221
|
+
create_and_send_impression_for_usage_tracking(settings, feature_key, context)
|
|
222
|
+
|
|
198
223
|
return GetFlag(is_enabled=True, variables=variation.get_variables(), session_id=context.get_session_id(), uuid=context.get_vwo_uuid())
|
|
199
224
|
elif (
|
|
200
225
|
stored_data
|
|
@@ -217,7 +242,10 @@ class GetFlagApi:
|
|
|
217
242
|
decision["isUserPartOfCampaign"] = True
|
|
218
243
|
# network calls for holdouts that are newly added in settings and are not present in storage
|
|
219
244
|
# and return the updated not in holdout ids
|
|
245
|
+
initial_size = len(stored_data.get("notInHoldoutId") or []) if stored_data else 0
|
|
220
246
|
updated_not_in_holdout_ids = send_network_calls_for_not_in_holdouts(settings, feature, context, decision, stored_data, storage_service)
|
|
247
|
+
if updated_not_in_holdout_ids and len(updated_not_in_holdout_ids) > initial_size:
|
|
248
|
+
is_variation_shown_fired = True
|
|
221
249
|
# push the updated not in holdout ids to the notInHoldoutIds array
|
|
222
250
|
notInHoldoutIds.extend(updated_not_in_holdout_ids)
|
|
223
251
|
|
|
@@ -236,6 +264,12 @@ class GetFlagApi:
|
|
|
236
264
|
if feature is None:
|
|
237
265
|
LogManager.get_instance().error_log("FEATURE_NOT_FOUND", data={"featureKey": feature_key}, debug_data = debug_event_props)
|
|
238
266
|
is_enabled = False
|
|
267
|
+
|
|
268
|
+
# Case: Feature not found
|
|
269
|
+
# If usage tracking is enabled, send usage tracking impression
|
|
270
|
+
if settings.get_is_tracking_usage_enabled() and not is_variation_shown_fired:
|
|
271
|
+
create_and_send_impression_for_usage_tracking(settings, feature_key, context)
|
|
272
|
+
|
|
239
273
|
return GetFlag(is_enabled=is_enabled, variables=variables, session_id=context.get_session_id(), uuid=context.get_vwo_uuid())
|
|
240
274
|
|
|
241
275
|
if context.get_session_id() is None:
|
|
@@ -315,6 +349,9 @@ class GetFlagApi:
|
|
|
315
349
|
else:
|
|
316
350
|
batchPayload.extend(holdout_payloads)
|
|
317
351
|
|
|
352
|
+
if holdout_payloads and len(holdout_payloads) > 0:
|
|
353
|
+
is_variation_shown_fired = True
|
|
354
|
+
|
|
318
355
|
roll_out_rules = get_specific_rules_based_on_type(
|
|
319
356
|
feature, CampaignTypeEnum.ROLLOUT.value
|
|
320
357
|
)
|
|
@@ -390,9 +427,13 @@ class GetFlagApi:
|
|
|
390
427
|
and payload is not None
|
|
391
428
|
and len(payload) > 0
|
|
392
429
|
):
|
|
430
|
+
# set is_variation_shown_fired to true as the rollout impression is sent
|
|
431
|
+
is_variation_shown_fired = True
|
|
393
432
|
send_impression_for_variation_shown(payload, context)
|
|
394
433
|
else:
|
|
395
434
|
if payload is not None and len(payload) > 0:
|
|
435
|
+
# set is_variation_shown_fired to true as the rollout impression is sent
|
|
436
|
+
is_variation_shown_fired = True
|
|
396
437
|
batchPayload.append(payload)
|
|
397
438
|
|
|
398
439
|
if not roll_out_rules:
|
|
@@ -435,9 +476,15 @@ class GetFlagApi:
|
|
|
435
476
|
and payload is not None
|
|
436
477
|
and len(payload) > 0
|
|
437
478
|
):
|
|
479
|
+
# set is_variation_shown_fired to true as the experiment impression is sent (for whitelisted user)
|
|
480
|
+
# this prevents sending usage tracking impression for the experiment campaign when checked at the bottom
|
|
481
|
+
is_variation_shown_fired = True
|
|
438
482
|
send_impression_for_variation_shown(payload, context)
|
|
439
483
|
else:
|
|
440
484
|
if payload is not None and len(payload) > 0:
|
|
485
|
+
# set is_variation_shown_fired to true as the experiment impression is sent (for whitelisted user)
|
|
486
|
+
# this prevents sending usage tracking impression for the experiment campaign when checked at the bottom
|
|
487
|
+
is_variation_shown_fired = True
|
|
441
488
|
batchPayload.append(payload)
|
|
442
489
|
|
|
443
490
|
is_enabled = True
|
|
@@ -481,9 +528,13 @@ class GetFlagApi:
|
|
|
481
528
|
and payload is not None
|
|
482
529
|
and len(payload) > 0
|
|
483
530
|
):
|
|
531
|
+
# set is_variation_shown_fired to true as the experiment impression is sent.
|
|
532
|
+
is_variation_shown_fired = True
|
|
484
533
|
send_impression_for_variation_shown(payload, context)
|
|
485
534
|
else:
|
|
486
535
|
if payload is not None and len(payload) > 0:
|
|
536
|
+
# set is_variation_shown_fired to true as the experiment impression is sent.
|
|
537
|
+
is_variation_shown_fired = True
|
|
487
538
|
batchPayload.append(payload)
|
|
488
539
|
|
|
489
540
|
if is_enabled:
|
|
@@ -545,9 +596,13 @@ class GetFlagApi:
|
|
|
545
596
|
and payload is not None
|
|
546
597
|
and len(payload) > 0
|
|
547
598
|
):
|
|
599
|
+
# set is_variation_shown_fired to true as the impact analysis impression is sent.
|
|
600
|
+
is_variation_shown_fired = True
|
|
548
601
|
send_impression_for_variation_shown(payload, context)
|
|
549
602
|
else:
|
|
550
603
|
if payload is not None and len(payload) > 0:
|
|
604
|
+
# set is_variation_shown_fired to true as the impact analysis impression is sent.
|
|
605
|
+
is_variation_shown_fired = True
|
|
551
606
|
batchPayload.append(payload)
|
|
552
607
|
|
|
553
608
|
if not SettingsManager.get_instance().is_gateway_service_provided:
|
|
@@ -555,6 +610,11 @@ class GetFlagApi:
|
|
|
555
610
|
batchPayload, settings.get_account_id(), settings.get_sdk_key()
|
|
556
611
|
)
|
|
557
612
|
|
|
613
|
+
# Send usage tracking call when no primary variationShown event was dispatched.
|
|
614
|
+
# If a primary event was fired, the server already has the usage tracking signal.
|
|
615
|
+
if settings.get_is_tracking_usage_enabled() and not is_variation_shown_fired:
|
|
616
|
+
create_and_send_impression_for_usage_tracking(settings, feature_key, context)
|
|
617
|
+
|
|
558
618
|
return GetFlag(is_enabled=is_enabled, variables=variables, session_id=context.get_session_id(), uuid=context.get_vwo_uuid())
|
|
559
619
|
|
|
560
620
|
def _update_integrations_decision_object(
|
|
@@ -36,6 +36,7 @@ class SettingsModel:
|
|
|
36
36
|
self._poll_interval = data.get("pollInterval", Constants.POLLING_INTERVAL)
|
|
37
37
|
self._is_web_connectivity_enabled = data.get("isWebConnectivityEnabled", True)
|
|
38
38
|
self._holdouts = [HoldoutModel(h) for h in data.get("holdouts", [])]
|
|
39
|
+
self._is_tracking_usage_enabled = data.get("isMAU", False)
|
|
39
40
|
|
|
40
41
|
# Getter methods for accessing private attributes
|
|
41
42
|
def get_features(self) -> List[FeatureModel]:
|
|
@@ -100,4 +101,10 @@ class SettingsModel:
|
|
|
100
101
|
self._holdouts = value
|
|
101
102
|
|
|
102
103
|
def get_holdouts(self) -> List[HoldoutModel]:
|
|
103
|
-
return self._holdouts if isinstance(self._holdouts, list) else []
|
|
104
|
+
return self._holdouts if isinstance(self._holdouts, list) else []
|
|
105
|
+
|
|
106
|
+
def get_is_tracking_usage_enabled(self) -> bool:
|
|
107
|
+
return self._is_tracking_usage_enabled
|
|
108
|
+
|
|
109
|
+
def set_is_tracking_usage_enabled(self, value: bool):
|
|
110
|
+
self._is_tracking_usage_enabled = value
|
{wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/user/context_model.py
RENAMED
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
from .context_vwo_model import ContextVWOModel
|
|
17
|
-
from typing import Dict, List
|
|
17
|
+
from typing import Any, Dict, List, Optional
|
|
18
|
+
from ...utils.data_type_util import is_object
|
|
18
19
|
from ...utils.uuid_util import get_uuid
|
|
19
20
|
from ...services.settings_manager import SettingsManager
|
|
20
21
|
from ...utils.function_util import get_current_unix_timestamp
|
|
@@ -33,6 +34,11 @@ class ContextModel:
|
|
|
33
34
|
self._vwo = ContextVWOModel(vwo_context_data) if vwo_context_data else None
|
|
34
35
|
self.post_segmentation_variables = context.get("post_segmentation_variables", [])
|
|
35
36
|
self.bucketingSeed = context.get("bucketingSeed")
|
|
37
|
+
self.platform_variables: Optional[Dict[str, Any]] = (
|
|
38
|
+
dict(context.get("platform_variables"))
|
|
39
|
+
if is_object(context.get("platform_variables"))
|
|
40
|
+
else None
|
|
41
|
+
)
|
|
36
42
|
self.session_id = context.get("session_id", None)
|
|
37
43
|
if self.session_id is None:
|
|
38
44
|
self.session_id = get_current_unix_timestamp()
|
|
@@ -96,4 +102,12 @@ class ContextModel:
|
|
|
96
102
|
self.session_id = session_id
|
|
97
103
|
|
|
98
104
|
def get_bucketing_seed(self) -> str:
|
|
99
|
-
return self.bucketingSeed
|
|
105
|
+
return self.bucketingSeed
|
|
106
|
+
|
|
107
|
+
def get_platform_variables(self) -> Optional[Dict[str, Any]]:
|
|
108
|
+
return self.platform_variables
|
|
109
|
+
|
|
110
|
+
def set_platform_variables(self, platform_variables: Optional[Dict[str, Any]]) -> None:
|
|
111
|
+
self.platform_variables = (
|
|
112
|
+
dict(platform_variables) if is_object(platform_variables) else None
|
|
113
|
+
)
|
|
@@ -24,6 +24,10 @@ from ....models.user.context_model import ContextModel
|
|
|
24
24
|
from ....utils.gateway_service_util import get_query_params, get_from_gateway_service
|
|
25
25
|
from ....constants.Constants import Constants
|
|
26
26
|
from ....enums.api_enum import ApiEnum
|
|
27
|
+
from ..utils.web_testing_segment_util import (
|
|
28
|
+
dsl_contains_campaign_variation_node,
|
|
29
|
+
is_web_testing_campaigns_absent_or_null,
|
|
30
|
+
)
|
|
27
31
|
|
|
28
32
|
|
|
29
33
|
class SegmentationManager:
|
|
@@ -115,4 +119,11 @@ class SegmentationManager:
|
|
|
115
119
|
:param properties: The properties to validate against.
|
|
116
120
|
:return: True if segmentation is valid, otherwise False.
|
|
117
121
|
"""
|
|
122
|
+
# if the dsl contains a campaign variation node, then we need to check if the campaign variation is valid
|
|
123
|
+
if dsl_contains_campaign_variation_node(dsl):
|
|
124
|
+
evaluation_context = getattr(self.evaluator, "context", None)
|
|
125
|
+
if evaluation_context is None or is_web_testing_campaigns_absent_or_null(
|
|
126
|
+
evaluation_context
|
|
127
|
+
):
|
|
128
|
+
return False
|
|
118
129
|
return self.evaluator.is_segmentation_valid(dsl, properties)
|
|
@@ -68,6 +68,10 @@ class SegmentEvaluator:
|
|
|
68
68
|
return SegmentOperandEvaluator().evaluate_string_operand_dsl(
|
|
69
69
|
sub_dsl, self.context, SegmentOperatorValueEnum.OS_VERSION.value
|
|
70
70
|
)
|
|
71
|
+
elif operator == SegmentOperatorValueEnum.WEB_CAMPAIGN_VARIATION.value:
|
|
72
|
+
return SegmentOperandEvaluator().evaluate_campaign_variation_dsl(
|
|
73
|
+
sub_dsl, self.context
|
|
74
|
+
)
|
|
71
75
|
else:
|
|
72
76
|
return False
|
|
73
77
|
|
|
@@ -16,6 +16,11 @@
|
|
|
16
16
|
import re
|
|
17
17
|
from typing import Dict, Any
|
|
18
18
|
from ..utils.segment_util import get_key_value, match_with_regex
|
|
19
|
+
from ..utils.web_testing_segment_util import (
|
|
20
|
+
coerce_campaign_variation_operand_to_str,
|
|
21
|
+
parse_web_testing_campaigns_from_context,
|
|
22
|
+
evaluate_web_testing_campaign_variation,
|
|
23
|
+
)
|
|
19
24
|
from ..enums.segment_operand_regex_enum import SegmentOperandRegexEnum
|
|
20
25
|
from ..enums.segment_operand_value_enum import SegmentOperandValueEnum
|
|
21
26
|
from ..enums.segment_operator_value_enum import SegmentOperatorValueEnum
|
|
@@ -84,6 +89,25 @@ class SegmentOperandEvaluator:
|
|
|
84
89
|
operand_type, processed_values["operand_value"], tag_value
|
|
85
90
|
)
|
|
86
91
|
|
|
92
|
+
def evaluate_campaign_variation_dsl(
|
|
93
|
+
self, dsl_operand_value: Any, context: ContextModel
|
|
94
|
+
) -> bool:
|
|
95
|
+
"""
|
|
96
|
+
Evaluates a campaignVariation DSL operand against the user's web testing campaign assignments.
|
|
97
|
+
|
|
98
|
+
:param dsl_operand_value: The raw operand value from the DSL (e.g. "123_1", "!123", 100).
|
|
99
|
+
:param context: The context object containing platform_variables.web_testing_campaigns.
|
|
100
|
+
:return: True if the segment condition is satisfied, otherwise False.
|
|
101
|
+
"""
|
|
102
|
+
operand_str = coerce_campaign_variation_operand_to_str(dsl_operand_value)
|
|
103
|
+
if operand_str is None:
|
|
104
|
+
return False
|
|
105
|
+
|
|
106
|
+
operand_str = operand_str.strip()
|
|
107
|
+
campaigns_map = parse_web_testing_campaigns_from_context(context)
|
|
108
|
+
result, _invalid = evaluate_web_testing_campaign_variation(operand_str, campaigns_map)
|
|
109
|
+
return result
|
|
110
|
+
|
|
87
111
|
def evaluate_user_dsl(self, dsl_operand_value, properties):
|
|
88
112
|
"""
|
|
89
113
|
Evaluates a user DSL expression to check if a user ID is in a specified List.
|