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.
Files changed (152) hide show
  1. {wingify_fme_python_sdk-1.50.0/wingify_fme_python_sdk.egg-info → wingify_fme_python_sdk-1.60.0}/PKG-INFO +22 -1
  2. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/README.md +21 -0
  3. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/setup.py +2 -2
  4. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/api/get_flag_api.py +61 -1
  5. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/constants/Constants.py +1 -1
  6. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/enums/event_enum.py +1 -0
  7. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/settings/settings_model.py +8 -1
  8. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/user/context_model.py +16 -2
  9. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/segmentation_evaluator/core/segmentation_manager.py +11 -0
  10. {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
  11. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/segmentation_evaluator/evaluators/segment_evaluator.py +4 -0
  12. {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
  13. wingify_fme_python_sdk-1.60.0/wingify/packages/segmentation_evaluator/utils/web_testing_segment_util.py +295 -0
  14. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/resources/debug-messages.json +2 -1
  15. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/resources/error-messages.json +8 -1
  16. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/impression_util.py +69 -1
  17. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0/wingify_fme_python_sdk.egg-info}/PKG-INFO +22 -1
  18. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify_fme_python_sdk.egg-info/SOURCES.txt +1 -0
  19. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/LICENSE +0 -0
  20. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/MANIFEST.in +0 -0
  21. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/NOTICE +0 -0
  22. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/requirements-dev.txt +0 -0
  23. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/requirements.txt +0 -0
  24. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/setup.cfg +0 -0
  25. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/vwo/__init__.py +0 -0
  26. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/vwo/vwo.py +0 -0
  27. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/vwo/vwo_builder.py +0 -0
  28. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/vwo/vwo_client.py +0 -0
  29. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/__init__.py +0 -0
  30. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/api/__init__.py +0 -0
  31. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/api/set_attribute_api.py +0 -0
  32. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/api/track_api.py +0 -0
  33. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/constants/__init__.py +0 -0
  34. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/decorators/__init__.py +0 -0
  35. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/decorators/storage_decorator.py +0 -0
  36. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/enums/__init__.py +0 -0
  37. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/enums/api_enum.py +0 -0
  38. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/enums/campaign_type_enum.py +0 -0
  39. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/enums/debug_category_enum.py +0 -0
  40. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/enums/headers_enum.py +0 -0
  41. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/enums/hooks_enum.py +0 -0
  42. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/enums/status_enum.py +0 -0
  43. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/enums/storage_enum.py +0 -0
  44. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/enums/url_enum.py +0 -0
  45. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/Alias.py +0 -0
  46. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/AliasSetResponse.py +0 -0
  47. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/__init__.py +0 -0
  48. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/batch_event_data.py +0 -0
  49. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/campaign/__init__.py +0 -0
  50. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/campaign/campaign_model.py +0 -0
  51. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/campaign/feature_model.py +0 -0
  52. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/campaign/holdout_model.py +0 -0
  53. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/campaign/impact_campaign_model.py +0 -0
  54. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/campaign/metric_model.py +0 -0
  55. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/campaign/rule_model.py +0 -0
  56. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/campaign/variable_model.py +0 -0
  57. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/campaign/variation_model.py +0 -0
  58. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/gateway_service_model.py +0 -0
  59. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/schemas/__init__.py +0 -0
  60. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/schemas/campaign_group_schema.py +0 -0
  61. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/schemas/campaign_metric_schema.py +0 -0
  62. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/schemas/campaign_schema.py +0 -0
  63. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/schemas/campaign_variation_schema.py +0 -0
  64. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/schemas/empty_object_schema.py +0 -0
  65. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/schemas/feature_schema.py +0 -0
  66. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/schemas/group_schema.py +0 -0
  67. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/schemas/holdout_schema.py +0 -0
  68. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/schemas/rule_schema.py +0 -0
  69. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/schemas/settings_schema.py +0 -0
  70. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/schemas/variable_schema.py +0 -0
  71. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/settings/__init__.py +0 -0
  72. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/storage/__init__.py +0 -0
  73. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/storage/storage_model.py +0 -0
  74. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/user/__init__.py +0 -0
  75. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/user/context_vwo_model.py +0 -0
  76. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/user/get_flag.py +0 -0
  77. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/vwo_options_model.py +0 -0
  78. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/models/wingify_options_model.py +0 -0
  79. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/__init__.py +0 -0
  80. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/decision_maker/__init__.py +0 -0
  81. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/decision_maker/decision_maker.py +0 -0
  82. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/logger/__init__.py +0 -0
  83. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/logger/core/__init__.py +0 -0
  84. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/logger/core/log_manager.py +0 -0
  85. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/logger/core/log_transport_manager.py +0 -0
  86. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/logger/enums/__init__.py +0 -0
  87. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/logger/enums/log_level_enum.py +0 -0
  88. {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
  89. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/logger/log_message_builder.py +0 -0
  90. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/logger/logger.py +0 -0
  91. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/logger/transports/__init__.py +0 -0
  92. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/logger/transports/console_transport.py +0 -0
  93. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/network_layer/__init__.py +0 -0
  94. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/network_layer/client/__init__.py +0 -0
  95. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/network_layer/client/network_client.py +0 -0
  96. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/network_layer/handlers/__init__.py +0 -0
  97. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/network_layer/handlers/request_handler.py +0 -0
  98. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/network_layer/manager/__init__.py +0 -0
  99. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/network_layer/manager/network_manager.py +0 -0
  100. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/network_layer/models/__init__.py +0 -0
  101. {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
  102. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/network_layer/models/request_model.py +0 -0
  103. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/network_layer/models/response_model.py +0 -0
  104. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/segmentation_evaluator/__init__.py +0 -0
  105. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/segmentation_evaluator/core/__init__.py +0 -0
  106. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/segmentation_evaluator/enums/__init__.py +0 -0
  107. {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
  108. {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
  109. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/segmentation_evaluator/evaluators/__init__.py +0 -0
  110. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/segmentation_evaluator/utils/__init__.py +0 -0
  111. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/segmentation_evaluator/utils/segment_util.py +0 -0
  112. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/storage/__init__.py +0 -0
  113. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/storage/connector.py +0 -0
  114. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/packages/storage/storage.py +0 -0
  115. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/resources/__init__.py +0 -0
  116. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/resources/info-message.json +0 -0
  117. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/resources/trace-messages.json +0 -0
  118. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/resources/warn-messages.json +0 -0
  119. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/services/__init__.py +0 -0
  120. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/services/batch_event_queue.py +0 -0
  121. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/services/campaign_decision_service.py +0 -0
  122. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/services/hooks_manager.py +0 -0
  123. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/services/settings_manager.py +0 -0
  124. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/services/storage_service.py +0 -0
  125. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/services/url_service.py +0 -0
  126. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/__init__.py +0 -0
  127. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/aliasing_util.py +0 -0
  128. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/brand_context.py +0 -0
  129. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/brand_util.py +0 -0
  130. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/campaign_util.py +0 -0
  131. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/data_type_util.py +0 -0
  132. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/debugger_service_util.py +0 -0
  133. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/decision_util.py +0 -0
  134. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/event_util.py +0 -0
  135. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/function_util.py +0 -0
  136. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/gateway_service_util.py +0 -0
  137. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/holdout_util.py +0 -0
  138. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/log_message_util.py +0 -0
  139. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/meg_util.py +0 -0
  140. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/model_utils.py +0 -0
  141. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/network_util.py +0 -0
  142. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/rule_evaluation_util.py +0 -0
  143. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/settings_util.py +0 -0
  144. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/usage_stats_util.py +0 -0
  145. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/utils/uuid_util.py +0 -0
  146. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/vwo_client.py +0 -0
  147. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/wingify.py +0 -0
  148. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/wingify_builder.py +0 -0
  149. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify/wingify_client.py +0 -0
  150. {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
  151. {wingify_fme_python_sdk-1.50.0 → wingify_fme_python_sdk-1.60.0}/wingify_fme_python_sdk.egg-info/requires.txt +0 -0
  152. {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.50.0
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["vwo"])
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.50.0",
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
- send_network_calls_for_not_in_holdouts(settings, feature, context, decision, stored_data, storage_service)
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(
@@ -17,7 +17,7 @@ class Constants:
17
17
  # TODO: read from setup.py
18
18
  sdk_meta = {
19
19
  "name": "vwo-fme-python-sdk",
20
- "version": "1.50.0",
20
+ "version": "1.60.0",
21
21
  }
22
22
 
23
23
  SDK_VERSION = sdk_meta["version"]
@@ -23,3 +23,4 @@ class EventEnum(Enum):
23
23
  SDK_INIT_EVENT = "vwo_fmeSdkInit"
24
24
  USAGE_STATS = "vwo_sdkUsageStats"
25
25
  DEBUGGER_EVENT = "vwo_sdkDebug"
26
+ TRACK_USAGE = "vwo_feTrackUsage"
@@ -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
@@ -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)
@@ -34,3 +34,4 @@ class SegmentOperatorValueEnum(Enum):
34
34
  IP = "ip_address"
35
35
  BROWSER_VERSION = "browser_version"
36
36
  OS_VERSION = "os_version"
37
+ WEB_CAMPAIGN_VARIATION = "campaignVariation"
@@ -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.