vwo-sdk 1.14.0 → 1.22.0
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.
- checksums.yaml +4 -4
- data/lib/abc.txt +28 -0
- data/lib/vwo/constants.rb +5 -2
- data/lib/vwo/core/bucketer.rb +33 -5
- data/lib/vwo/core/variation_decider.rb +420 -131
- data/lib/vwo/enums.rb +6 -1
- data/lib/vwo/logger.rb +4 -2
- data/lib/vwo/schemas/settings_file.rb +32 -1
- data/lib/vwo/services/segment_evaluator.rb +4 -2
- data/lib/vwo/utils/campaign.rb +60 -2
- data/lib/vwo/utils/feature.rb +2 -0
- data/lib/vwo.rb +193 -193
- metadata +29 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 84d01560f605a69e8ed011a19ba4e3aae0f67341fd653a562d817801d6a55073
|
4
|
+
data.tar.gz: b1355bef1b2b095774c702a6757c1a7141e2cf9758d0a1b8a0510111a8ee597a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cc2c8a411e3c0a674ab2bc00e540b3e49a8dd943ec50aa04fba04839ef6f3e255fc275529f872aa5ca73dfe893cb43e8853a0875cc3dda9330554b6491590947
|
7
|
+
data.tar.gz: 5482e79564a365ec8fe4012b25997096352129e915daca28774c790c416a528302ea1f0bb685ef559b9ee0846b0229957a8f6ab77008f4e48222a7cae5892699
|
data/lib/abc.txt
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
{"user_id"=>"Ashley", "variation_name"=>"Control", "campaign_key"=>"DEV_TEST_162"}
|
2
|
+
GOT_ELIGIBLE_CAMPAIGNS
|
3
|
+
{"user_id"=>"Ashley", "variation_name"=>"Control", "campaign_key"=>"DEV_TEST_162"}
|
4
|
+
GOT_ELIGIBLE_CAMPAIGNS
|
5
|
+
{"user_id"=>"Ashley", "variation_name"=>"Control", "campaign_key"=>"DEV_TEST_162"}
|
6
|
+
GOT_ELIGIBLE_CAMPAIGNS
|
7
|
+
{"user_id"=>"Ashley", "variation_name"=>"Control", "campaign_key"=>"DEV_TEST_162"}
|
8
|
+
GOT_ELIGIBLE_CAMPAIGNS
|
9
|
+
{"user_id"=>"Ashley", "variation_name"=>"Control", "campaign_key"=>"DEV_TEST_162"}
|
10
|
+
GOT_ELIGIBLE_CAMPAIGNS
|
11
|
+
{"user_id"=>"Ashley", "variation_name"=>"Control", "campaign_key"=>"DEV_TEST_162"}
|
12
|
+
GOT_ELIGIBLE_CAMPAIGNS
|
13
|
+
{"user_id"=>"Ashley", "variation_name"=>"Control", "campaign_key"=>"DEV_TEST_162"}
|
14
|
+
GOT_ELIGIBLE_CAMPAIGNS
|
15
|
+
{"user_id"=>"Ashley", "variation_name"=>"Control", "campaign_key"=>"DEV_TEST_162"}
|
16
|
+
GOT_ELIGIBLE_CAMPAIGNS
|
17
|
+
{"user_id"=>"Ashley", "variation_name"=>"Control", "campaign_key"=>"DEV_TEST_162"}
|
18
|
+
GOT_ELIGIBLE_CAMPAIGNS
|
19
|
+
{"user_id"=>"Ashley", "variation_name"=>"Control", "campaign_key"=>"DEV_TEST_162"}
|
20
|
+
GOT_ELIGIBLE_CAMPAIGNS
|
21
|
+
{"user_id"=>"Ashley", "variation_name"=>"Control", "campaign_key"=>"DEV_TEST_162"}
|
22
|
+
GOT_ELIGIBLE_CAMPAIGNS
|
23
|
+
{"user_id"=>"Ashley", "variation_name"=>"Control", "campaign_key"=>"DEV_TEST_162"}
|
24
|
+
GOT_ELIGIBLE_CAMPAIGNS
|
25
|
+
{"user_id"=>"Ashley", "variation_name"=>"Control", "campaign_key"=>"DEV_TEST_162"}
|
26
|
+
GOT_ELIGIBLE_CAMPAIGNS
|
27
|
+
{"user_id"=>"Ashley", "variation_name"=>"Control", "campaign_key"=>"DEV_TEST_162"}
|
28
|
+
GOT_ELIGIBLE_CAMPAIGNS
|
data/lib/vwo/constants.rb
CHANGED
@@ -19,6 +19,7 @@ class VWO
|
|
19
19
|
SEED_VALUE = 1
|
20
20
|
MAX_TRAFFIC_PERCENT = 100
|
21
21
|
MAX_TRAFFIC_VALUE = 10_000
|
22
|
+
MAX_RANGE = 10000
|
22
23
|
STATUS_RUNNING = 'RUNNING'
|
23
24
|
# rubocop:disable Style/ExpandPathArguments
|
24
25
|
LIBRARY_PATH = File.expand_path('../..', __FILE__)
|
@@ -26,7 +27,7 @@ class VWO
|
|
26
27
|
HTTP_PROTOCOL = 'http://'
|
27
28
|
HTTPS_PROTOCOL = 'https://'
|
28
29
|
URL_NAMESPACE = '6ba7b811-9dad-11d1-80b4-00c04fd430c8'
|
29
|
-
SDK_VERSION = '1.
|
30
|
+
SDK_VERSION = '1.22.0'
|
30
31
|
SDK_NAME = 'ruby'
|
31
32
|
VWO_DELIMITER = '_vwo_'
|
32
33
|
MAX_EVENTS_PER_REQUEST = 5000
|
@@ -68,6 +69,7 @@ class VWO
|
|
68
69
|
INTEGER = 'integer'
|
69
70
|
DOUBLE = 'double'
|
70
71
|
BOOLEAN = 'boolean'
|
72
|
+
JSON = 'json'
|
71
73
|
end
|
72
74
|
|
73
75
|
module Hooks
|
@@ -80,7 +82,8 @@ class VWO
|
|
80
82
|
'string' => [String],
|
81
83
|
'integer' => [Integer],
|
82
84
|
'double' => [Float],
|
83
|
-
'boolean' => [TrueClass, FalseClass]
|
85
|
+
'boolean' => [TrueClass, FalseClass],
|
86
|
+
'json' => [Hash]
|
84
87
|
}
|
85
88
|
|
86
89
|
GOAL_TYPES = {
|
data/lib/vwo/core/bucketer.rb
CHANGED
@@ -60,7 +60,7 @@ class VWO
|
|
60
60
|
end
|
61
61
|
|
62
62
|
traffic_allocation = campaign['percentTraffic']
|
63
|
-
value_assigned_to_user = get_bucket_value_for_user(user_id)
|
63
|
+
value_assigned_to_user = get_bucket_value_for_user(user_id, campaign)
|
64
64
|
is_user_part = (value_assigned_to_user != 0) && value_assigned_to_user <= traffic_allocation
|
65
65
|
@logger.log(
|
66
66
|
LogLevelEnum::INFO,
|
@@ -94,7 +94,11 @@ class VWO
|
|
94
94
|
return
|
95
95
|
end
|
96
96
|
|
97
|
-
|
97
|
+
user_id_for_hash_value = user_id
|
98
|
+
if campaign[:isBucketingSeedEnabled]
|
99
|
+
user_id_for_hash_value = campaign[:id].to_s + "_" + user_id
|
100
|
+
end
|
101
|
+
hash_value = MurmurHash3::V32.str_hash(user_id_for_hash_value, SEED_VALUE) & U_MAX_32_BIT
|
98
102
|
normalize = MAX_TRAFFIC_VALUE.to_f / campaign['percentTraffic']
|
99
103
|
multiplier = normalize / 100
|
100
104
|
bucket_value = get_bucket_value(
|
@@ -136,10 +140,17 @@ class VWO
|
|
136
140
|
# User by hashing the userId by murmurHash and scaling it down.
|
137
141
|
#
|
138
142
|
# @param[String] :user_id The unique ID assigned to User
|
143
|
+
# @param[String] :campaign Campaign data
|
139
144
|
# @return[Integer] The bucket Value allotted to User
|
140
145
|
# (between 1 to $this->$MAX_TRAFFIC_PERCENT)
|
141
|
-
def get_bucket_value_for_user(user_id)
|
142
|
-
|
146
|
+
def get_bucket_value_for_user(user_id, campaign = {}, group_id = nil, disable_logs = false)
|
147
|
+
user_id_for_hash_value = user_id
|
148
|
+
if group_id
|
149
|
+
user_id_for_hash_value = group_id.to_s + "_" + user_id
|
150
|
+
elsif campaign[:isBucketingSeedEnabled]
|
151
|
+
user_id_for_hash_value = campaign[:id].to_s + "_" + user_id
|
152
|
+
end
|
153
|
+
hash_value = MurmurHash3::V32.str_hash(user_id_for_hash_value, SEED_VALUE) & U_MAX_32_BIT
|
143
154
|
bucket_value = get_bucket_value(hash_value, MAX_TRAFFIC_PERCENT)
|
144
155
|
|
145
156
|
@logger.log(
|
@@ -150,7 +161,8 @@ class VWO
|
|
150
161
|
hash_value: hash_value,
|
151
162
|
bucket_value: bucket_value,
|
152
163
|
user_id: user_id
|
153
|
-
)
|
164
|
+
),
|
165
|
+
disable_logs
|
154
166
|
)
|
155
167
|
bucket_value
|
156
168
|
end
|
@@ -168,6 +180,22 @@ class VWO
|
|
168
180
|
multiplied_value = (max_value * ratio + 1) * multiplier
|
169
181
|
multiplied_value.to_i
|
170
182
|
end
|
183
|
+
|
184
|
+
# Returns a campaign by checking the Start and End Bucket Allocations of each campaign.
|
185
|
+
#
|
186
|
+
# @param[Integer] :range_for_campaigns the bucket value of the user
|
187
|
+
# @param[Hash] :campaigns The bucket Value of the user
|
188
|
+
# @return[Hash|nil]
|
189
|
+
#
|
190
|
+
def get_campaign_using_range(range_for_campaigns, campaigns)
|
191
|
+
range_for_campaigns = range_for_campaigns * 100
|
192
|
+
campaigns.each do |campaign|
|
193
|
+
if campaign["max_range"] && campaign["max_range"] >= range_for_campaigns && campaign["min_range"] <= range_for_campaigns
|
194
|
+
return campaign
|
195
|
+
end
|
196
|
+
end
|
197
|
+
nil
|
198
|
+
end
|
171
199
|
end
|
172
200
|
end
|
173
201
|
end
|
@@ -66,6 +66,8 @@ class VWO
|
|
66
66
|
|
67
67
|
return unless campaign
|
68
68
|
|
69
|
+
is_campaign_part_of_group = @settings_file && is_part_of_group(@settings_file, campaign["id"])
|
70
|
+
|
69
71
|
@has_stored_variation = false
|
70
72
|
decision = {
|
71
73
|
:campaign_id => campaign['id'],
|
@@ -93,65 +95,23 @@ class VWO
|
|
93
95
|
:vwo_user_id => generator_for(user_id, @settings_file['accountId'])
|
94
96
|
}
|
95
97
|
|
96
|
-
if
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
campaign_key,
|
102
|
-
variation_targeting_variables
|
103
|
-
)
|
104
|
-
status = if variation
|
105
|
-
StatusEnum::PASSED
|
106
|
-
else
|
107
|
-
StatusEnum::FAILED
|
108
|
-
end
|
109
|
-
|
110
|
-
@logger.log(
|
111
|
-
LogLevelEnum::INFO,
|
112
|
-
format(
|
113
|
-
LogMessageEnum::InfoMessages::SEGMENTATION_STATUS,
|
114
|
-
file: FILE,
|
115
|
-
campaign_key: campaign_key,
|
116
|
-
user_id: user_id,
|
117
|
-
status: status,
|
118
|
-
custom_variables: variation_targeting_variables,
|
119
|
-
variation_name: status == StatusEnum::PASSED ? "and #{variation['name']} is Assigned" : ' ',
|
120
|
-
segmentation_type: SegmentationTypeEnum::WHITELISTING,
|
121
|
-
api_name: api_name
|
122
|
-
)
|
123
|
-
)
|
124
|
-
|
125
|
-
if variation
|
126
|
-
if campaign['type'] == CampaignTypes::VISUAL_AB || campaign['type'] == CampaignTypes::FEATURE_TEST
|
127
|
-
decision[:variation_name] = variation['name']
|
128
|
-
decision[:variation_id] = variation['id']
|
129
|
-
if campaign['type'] == CampaignTypes::FEATURE_TEST
|
130
|
-
decision[:is_feature_enabled] = variation['isFeatureEnabled']
|
131
|
-
elsif campaign['type'] == CampaignTypes::VISUAL_AB
|
132
|
-
decision[:is_user_whitelisted] = !!variation['name']
|
133
|
-
end
|
134
|
-
end
|
135
|
-
@hooks_manager.execute(decision)
|
136
|
-
end
|
137
|
-
|
138
|
-
return variation if variation && variation['name']
|
139
|
-
else
|
140
|
-
@logger.log(
|
141
|
-
LogLevelEnum::INFO,
|
142
|
-
format(
|
143
|
-
LogMessageEnum::InfoMessages::WHITELISTING_SKIPPED,
|
144
|
-
file: FILE,
|
145
|
-
campaign_key: campaign_key,
|
146
|
-
user_id: user_id,
|
147
|
-
api_name: api_name
|
148
|
-
)
|
149
|
-
)
|
98
|
+
if is_campaign_part_of_group
|
99
|
+
group_id = @settings_file["campaignGroups"][campaign["id"].to_s]
|
100
|
+
decision[:group_id] = group_id
|
101
|
+
group_name = @settings_file["groups"][group_id.to_s]["name"]
|
102
|
+
decision[:group_name] = group_name
|
150
103
|
end
|
151
104
|
|
105
|
+
# evaluate whitelisting
|
106
|
+
variation = get_variation_if_whitelisting_passed(user_id, campaign, variation_targeting_variables, api_name, decision, true)
|
107
|
+
return variation if variation && variation['name']
|
108
|
+
|
152
109
|
user_campaign_map = get_user_storage(user_id, campaign_key)
|
153
110
|
variation = get_stored_variation(user_id, campaign_key, user_campaign_map) if valid_hash?(user_campaign_map)
|
154
|
-
|
111
|
+
|
112
|
+
if variation
|
113
|
+
variation = variation.dup # deep copy
|
114
|
+
end
|
155
115
|
|
156
116
|
if variation
|
157
117
|
if valid_string?(user_campaign_map['goal_identifier']) && api_name == ApiMethods::TRACK
|
@@ -191,8 +151,7 @@ class VWO
|
|
191
151
|
)
|
192
152
|
)
|
193
153
|
|
194
|
-
if ([ApiMethods::TRACK, ApiMethods::GET_VARIATION_NAME, ApiMethods::GET_FEATURE_VARIABLE_VALUE].include? api_name) &&
|
195
|
-
@user_storage_service && campaign['type'] != CampaignTypes::FEATURE_ROLLOUT
|
154
|
+
if ([ApiMethods::TRACK, ApiMethods::GET_VARIATION_NAME, ApiMethods::GET_FEATURE_VARIABLE_VALUE].include? api_name) && @user_storage_service
|
196
155
|
@logger.log(
|
197
156
|
LogLevelEnum::DEBUG,
|
198
157
|
format(
|
@@ -220,82 +179,106 @@ class VWO
|
|
220
179
|
end
|
221
180
|
|
222
181
|
# Pre-segmentation
|
182
|
+
is_presegmentation = check_presegmentation(campaign, user_id, custom_variables, api_name)
|
183
|
+
is_presegmentation_and_traffic_passed = is_presegmentation && @bucketer.user_part_of_campaign?(user_id, campaign)
|
184
|
+
unless is_presegmentation_and_traffic_passed
|
185
|
+
return nil
|
186
|
+
end
|
223
187
|
|
224
|
-
|
225
|
-
|
188
|
+
if is_presegmentation_and_traffic_passed && is_campaign_part_of_group
|
189
|
+
group_campaigns = get_group_campaigns(@settings_file, group_id)
|
190
|
+
|
191
|
+
if group_campaigns
|
192
|
+
is_any_campaign_whitelisted_or_stored = check_whitelisting_or_storage_for_grouped_campaigns(user_id, campaign, group_campaigns, group_name, variation_targeting_variables, true)
|
193
|
+
|
194
|
+
if is_any_campaign_whitelisted_or_stored
|
195
|
+
@logger.log(
|
196
|
+
LogLevelEnum::INFO,
|
197
|
+
format(
|
198
|
+
LogMessageEnum::InfoMessages::CALLED_CAMPAIGN_NOT_WINNER,
|
199
|
+
file: FILE,
|
200
|
+
campaign_key: campaign_key,
|
201
|
+
user_id: user_id,
|
202
|
+
group_name: group_name
|
203
|
+
)
|
204
|
+
)
|
205
|
+
return nil
|
206
|
+
end
|
207
|
+
|
208
|
+
eligible_campaigns = get_eligible_campaigns(user_id, group_campaigns, campaign, custom_variables)
|
209
|
+
non_eligible_campaigns_key = get_non_eligible_campaigns_key(eligible_campaigns, group_campaigns)
|
210
|
+
|
211
|
+
@logger.log(
|
212
|
+
LogLevelEnum::DEBUG,
|
213
|
+
format(
|
214
|
+
LogMessageEnum::DebugMessages::GOT_ELIGIBLE_CAMPAIGNS,
|
215
|
+
file: FILE,
|
216
|
+
user_id: user_id,
|
217
|
+
eligible_campaigns_key: get_eligible_campaigns_key(eligible_campaigns).join(","),
|
218
|
+
ineligible_campaigns_log_text: non_eligible_campaigns_key ? ("campaigns:" + non_eligible_campaigns_key.join("'")) : "no campaigns",
|
219
|
+
group_name: group_name
|
220
|
+
)
|
221
|
+
)
|
226
222
|
|
227
|
-
if is_valid_segments
|
228
|
-
unless custom_variables
|
229
223
|
@logger.log(
|
230
224
|
LogLevelEnum::INFO,
|
231
225
|
format(
|
232
|
-
LogMessageEnum::InfoMessages::
|
226
|
+
LogMessageEnum::InfoMessages::GOT_ELIGIBLE_CAMPAIGNS,
|
233
227
|
file: FILE,
|
234
|
-
campaign_key: campaign_key,
|
235
228
|
user_id: user_id,
|
236
|
-
|
229
|
+
no_of_eligible_campaigns: eligible_campaigns.length,
|
230
|
+
no_of_group_campaigns: group_campaigns.length,
|
231
|
+
group_name: group_name
|
237
232
|
)
|
238
233
|
)
|
239
|
-
|
240
|
-
|
241
|
-
unless @segment_evaluator.evaluate(campaign_key, user_id, segments, custom_variables)
|
234
|
+
|
235
|
+
winner_campaign = get_winner_campaign(user_id, eligible_campaigns, group_id)
|
242
236
|
@logger.log(
|
243
237
|
LogLevelEnum::INFO,
|
244
238
|
format(
|
245
|
-
LogMessageEnum::InfoMessages::
|
246
|
-
file:
|
239
|
+
LogMessageEnum::InfoMessages::GOT_WINNER_CAMPAIGN,
|
240
|
+
file: FILE,
|
247
241
|
user_id: user_id,
|
248
|
-
campaign_key:
|
249
|
-
|
242
|
+
campaign_key: winner_campaign["key"],
|
243
|
+
group_name: group_name
|
250
244
|
)
|
251
245
|
)
|
252
|
-
|
246
|
+
|
247
|
+
if winner_campaign && winner_campaign["id"] == campaign["id"]
|
248
|
+
variation = get_variation_allotted(user_id, campaign, true)
|
249
|
+
if variation && variation['name']
|
250
|
+
save_user_storage(user_id, campaign_key, campaign['type'], variation['name'], goal_identifier, true) if variation['name']
|
251
|
+
else
|
252
|
+
return nil
|
253
|
+
end
|
254
|
+
else
|
255
|
+
@logger.log(
|
256
|
+
LogLevelEnum::INFO,
|
257
|
+
format(
|
258
|
+
LogMessageEnum::InfoMessages::CALLED_CAMPAIGN_NOT_WINNER,
|
259
|
+
file: FILE,
|
260
|
+
campaign_key: campaign_key,
|
261
|
+
user_id: user_id,
|
262
|
+
group_name: group_name
|
263
|
+
)
|
264
|
+
)
|
265
|
+
return nil
|
266
|
+
end
|
253
267
|
end
|
254
|
-
@logger.log(
|
255
|
-
LogLevelEnum::INFO,
|
256
|
-
format(
|
257
|
-
LogMessageEnum::InfoMessages::USER_PASSED_SEGMENTATION,
|
258
|
-
file: FileNameEnum::SegmentEvaluator,
|
259
|
-
user_id: user_id,
|
260
|
-
campaign_key: campaign_key,
|
261
|
-
custom_variables: custom_variables
|
262
|
-
)
|
263
|
-
)
|
264
|
-
else
|
265
|
-
@logger.log(
|
266
|
-
LogLevelEnum::INFO,
|
267
|
-
format(
|
268
|
-
LogMessageEnum::InfoMessages::SKIPPING_SEGMENTATION,
|
269
|
-
file: FILE,
|
270
|
-
campaign_key: campaign_key,
|
271
|
-
user_id: user_id,
|
272
|
-
api_name: api_name,
|
273
|
-
variation: ''
|
274
|
-
)
|
275
|
-
)
|
276
268
|
end
|
277
269
|
|
278
|
-
variation
|
279
|
-
|
280
|
-
if variation && variation['name']
|
281
|
-
save_user_storage(user_id, campaign_key, variation['name'], goal_identifier) if variation['name']
|
270
|
+
if variation.nil?
|
271
|
+
variation = get_variation_allotted(user_id, campaign)
|
282
272
|
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
campaign_key: campaign_key,
|
289
|
-
user_id: user_id,
|
290
|
-
variation_name: variation['name'],
|
291
|
-
campaign_type: campaign['type']
|
273
|
+
if variation && variation['name']
|
274
|
+
save_user_storage(user_id, campaign_key, campaign['type'], variation['name'], goal_identifier) if variation['name']
|
275
|
+
else
|
276
|
+
@logger.log(
|
277
|
+
LogLevelEnum::INFO,
|
278
|
+
format(LogMessageEnum::InfoMessages::NO_VARIATION_ALLOCATED, file: FILE, campaign_key: campaign_key, user_id: user_id)
|
292
279
|
)
|
293
|
-
|
294
|
-
|
295
|
-
@logger.log(
|
296
|
-
LogLevelEnum::INFO,
|
297
|
-
format(LogMessageEnum::InfoMessages::NO_VARIATION_ALLOCATED, file: FILE, campaign_key: campaign_key, user_id: user_id)
|
298
|
-
)
|
280
|
+
end
|
281
|
+
|
299
282
|
end
|
300
283
|
|
301
284
|
if variation
|
@@ -318,7 +301,7 @@ class VWO
|
|
318
301
|
#
|
319
302
|
# @return[Hash]
|
320
303
|
|
321
|
-
def get_variation_allotted(user_id, campaign)
|
304
|
+
def get_variation_allotted(user_id, campaign, disable_logs = false)
|
322
305
|
unless valid_value?(user_id)
|
323
306
|
@logger.log(
|
324
307
|
LogLevelEnum::ERROR,
|
@@ -338,7 +321,8 @@ class VWO
|
|
338
321
|
user_id: user_id,
|
339
322
|
campaign_key: campaign['key'],
|
340
323
|
method: 'get_variation_allotted'
|
341
|
-
)
|
324
|
+
),
|
325
|
+
disable_logs
|
342
326
|
)
|
343
327
|
variation
|
344
328
|
else
|
@@ -351,7 +335,8 @@ class VWO
|
|
351
335
|
user_id: user_id,
|
352
336
|
campaign_key: nil,
|
353
337
|
method: 'get_variation_allotted'
|
354
|
-
)
|
338
|
+
),
|
339
|
+
disable_logs
|
355
340
|
)
|
356
341
|
nil
|
357
342
|
end
|
@@ -410,13 +395,15 @@ class VWO
|
|
410
395
|
# @param[String] :campaign_key Unique campaign identifier
|
411
396
|
# @param[String] :variation_name Variation identifier
|
412
397
|
# @param[String] :goal_identifier The unique campaign's goal identifier
|
398
|
+
# @param[Boolean] :disable_logs optional: disable logs if True
|
413
399
|
# @return[Boolean] true if found otherwise false
|
414
400
|
|
415
|
-
def save_user_storage(user_id, campaign_key, variation_name, goal_identifier)
|
401
|
+
def save_user_storage(user_id, campaign_key, campaign_type, variation_name, goal_identifier, disable_logs = false)
|
416
402
|
unless @user_storage_service
|
417
403
|
@logger.log(
|
418
404
|
LogLevelEnum::DEBUG,
|
419
|
-
format(LogMessageEnum::DebugMessages::NO_USER_STORAGE_SERVICE_SAVE, file: FILE)
|
405
|
+
format(LogMessageEnum::DebugMessages::NO_USER_STORAGE_SERVICE_SAVE, file: FILE),
|
406
|
+
disable_logs
|
420
407
|
)
|
421
408
|
return false
|
422
409
|
end
|
@@ -432,13 +419,28 @@ class VWO
|
|
432
419
|
|
433
420
|
@logger.log(
|
434
421
|
LogLevelEnum::INFO,
|
435
|
-
format(LogMessageEnum::InfoMessages::SAVING_DATA_USER_STORAGE_SERVICE, file: FILE, user_id: user_id)
|
422
|
+
format(LogMessageEnum::InfoMessages::SAVING_DATA_USER_STORAGE_SERVICE, file: FILE, user_id: user_id),
|
423
|
+
disable_logs
|
424
|
+
)
|
425
|
+
|
426
|
+
@logger.log(
|
427
|
+
LogLevelEnum::INFO,
|
428
|
+
format(
|
429
|
+
LogMessageEnum::InfoMessages::VARIATION_ALLOCATED,
|
430
|
+
file: FILE,
|
431
|
+
campaign_key: campaign_key,
|
432
|
+
user_id: user_id,
|
433
|
+
variation_name: variation_name,
|
434
|
+
campaign_type: campaign_type
|
435
|
+
),
|
436
|
+
disable_logs
|
436
437
|
)
|
437
438
|
true
|
438
439
|
rescue StandardError
|
439
440
|
@logger.log(
|
440
441
|
LogLevelEnum::ERROR,
|
441
|
-
format(LogMessageEnum::ErrorMessages::SAVE_USER_STORAGE_SERVICE_FAILED, file: FILE, user_id: user_id)
|
442
|
+
format(LogMessageEnum::ErrorMessages::SAVE_USER_STORAGE_SERVICE_FAILED, file: FILE, user_id: user_id),
|
443
|
+
disable_logs
|
442
444
|
)
|
443
445
|
false
|
444
446
|
end
|
@@ -452,10 +454,11 @@ class VWO
|
|
452
454
|
# @param[String] :api_name The key Passed to identify the calling API
|
453
455
|
# @param[String] :campaign_key Unique campaign key
|
454
456
|
# @param[Hash] :variation_targeting_variables Key/value pair of Whitelisting Custom Attributes
|
457
|
+
# @param[Boolean] :disable_logs optional: disable logs if True
|
455
458
|
#
|
456
459
|
# @return[Hash]
|
457
460
|
|
458
|
-
def evaluate_whitelisting(user_id, campaign, api_name, campaign_key, variation_targeting_variables = {})
|
461
|
+
def evaluate_whitelisting(user_id, campaign, api_name, campaign_key, variation_targeting_variables = {}, disable_logs = false)
|
459
462
|
if variation_targeting_variables.nil?
|
460
463
|
variation_targeting_variables = { '_vwo_user_id' => user_id }
|
461
464
|
else
|
@@ -467,7 +470,7 @@ class VWO
|
|
467
470
|
segments = get_segments(variation)
|
468
471
|
is_valid_segments = valid_value?(segments)
|
469
472
|
if is_valid_segments
|
470
|
-
if @segment_evaluator.evaluate(campaign_key, user_id, segments, variation_targeting_variables)
|
473
|
+
if @segment_evaluator.evaluate(campaign_key, user_id, segments, variation_targeting_variables, disable_logs)
|
471
474
|
targeted_variations.push(variation)
|
472
475
|
status = StatusEnum::PASSED
|
473
476
|
else
|
@@ -485,7 +488,8 @@ class VWO
|
|
485
488
|
variation_name: variation['name'],
|
486
489
|
segmentation_type: SegmentationTypeEnum::WHITELISTING,
|
487
490
|
api_name: api_name
|
488
|
-
)
|
491
|
+
),
|
492
|
+
disable_logs
|
489
493
|
)
|
490
494
|
else
|
491
495
|
@logger.log(
|
@@ -497,7 +501,8 @@ class VWO
|
|
497
501
|
user_id: user_id,
|
498
502
|
api_name: api_name,
|
499
503
|
variation: variation['name']
|
500
|
-
)
|
504
|
+
),
|
505
|
+
disable_logs
|
501
506
|
)
|
502
507
|
end
|
503
508
|
end
|
@@ -522,7 +527,8 @@ class VWO
|
|
522
527
|
whitelisted_variation = @bucketer.get_variation(
|
523
528
|
targeted_variations_deep_clone,
|
524
529
|
@bucketer.get_bucket_value_for_user(
|
525
|
-
user_id
|
530
|
+
user_id,
|
531
|
+
campaign
|
526
532
|
)
|
527
533
|
)
|
528
534
|
else
|
@@ -550,18 +556,26 @@ class VWO
|
|
550
556
|
end
|
551
557
|
end
|
552
558
|
|
559
|
+
def scale_campaigns_weight(campaigns)
|
560
|
+
normalize_weight = 100/campaigns.length
|
561
|
+
campaigns.each do |campaign|
|
562
|
+
campaign['weight'] = normalize_weight
|
563
|
+
end
|
564
|
+
end
|
553
565
|
# Get the UserStorageData after looking up into get method
|
554
566
|
# Being provided via UserStorageService
|
555
567
|
#
|
556
568
|
# @param[String]: Unique user identifier
|
557
569
|
# @param[String]: Unique campaign key
|
570
|
+
# @param[Boolean] :disable_logs if True
|
558
571
|
# @return[Hash|Boolean]: user_storage data
|
559
572
|
|
560
|
-
def get_user_storage(user_id, campaign_key)
|
573
|
+
def get_user_storage(user_id, campaign_key, disable_logs = false)
|
561
574
|
unless @user_storage_service
|
562
575
|
@logger.log(
|
563
576
|
LogLevelEnum::DEBUG,
|
564
|
-
format(LogMessageEnum::DebugMessages::NO_USER_STORAGE_SERVICE_LOOKUP, file: FILE)
|
577
|
+
format(LogMessageEnum::DebugMessages::NO_USER_STORAGE_SERVICE_LOOKUP, file: FILE),
|
578
|
+
disable_logs
|
565
579
|
)
|
566
580
|
return false
|
567
581
|
end
|
@@ -574,13 +588,15 @@ class VWO
|
|
574
588
|
file: FILE,
|
575
589
|
user_id: user_id,
|
576
590
|
status: data.nil? ? 'Not Found' : 'Found'
|
577
|
-
)
|
591
|
+
),
|
592
|
+
disable_logs
|
578
593
|
)
|
579
594
|
data
|
580
595
|
rescue StandardError
|
581
596
|
@logger.log(
|
582
597
|
LogLevelEnum::ERROR,
|
583
|
-
format(LogMessageEnum::ErrorMessages::LOOK_UP_USER_STORAGE_SERVICE_FAILED, file: FILE, user_id: user_id)
|
598
|
+
format(LogMessageEnum::ErrorMessages::LOOK_UP_USER_STORAGE_SERVICE_FAILED, file: FILE, user_id: user_id),
|
599
|
+
disable_logs
|
584
600
|
)
|
585
601
|
false
|
586
602
|
end
|
@@ -590,10 +606,11 @@ class VWO
|
|
590
606
|
# @param[String] :user_id
|
591
607
|
# @param[String] :campaign_key campaign identified
|
592
608
|
# @param[Hash] :user_campaign_map BucketMap consisting of stored user variation
|
609
|
+
# @param[Boolean] :disable_logs if True
|
593
610
|
#
|
594
611
|
# @return[Object, nil] if found then variation settings object otherwise None
|
595
612
|
|
596
|
-
def get_stored_variation(user_id, campaign_key, user_campaign_map)
|
613
|
+
def get_stored_variation(user_id, campaign_key, user_campaign_map, disable_logs = false)
|
597
614
|
return unless user_campaign_map['campaign_key'] == campaign_key
|
598
615
|
|
599
616
|
variation_name = user_campaign_map['variation_name']
|
@@ -605,7 +622,8 @@ class VWO
|
|
605
622
|
campaign_key: campaign_key,
|
606
623
|
user_id: user_id,
|
607
624
|
variation_name: variation_name
|
608
|
-
)
|
625
|
+
),
|
626
|
+
disable_logs
|
609
627
|
)
|
610
628
|
|
611
629
|
get_campaign_variation(
|
@@ -614,6 +632,277 @@ class VWO
|
|
614
632
|
variation_name
|
615
633
|
)
|
616
634
|
end
|
635
|
+
|
636
|
+
# this function check whether pre-segmentation is passed or not
|
637
|
+
#
|
638
|
+
# @param[String] :user_id The unique key assigned to User
|
639
|
+
# @param[Hash] :campaign Campaign hash for Unique campaign key
|
640
|
+
# @param[Hash] :custom_variables Key/value pair for segmentation
|
641
|
+
# @param[String] :api_name The key Passed to identify the calling API
|
642
|
+
# @param[Boolean] :disable_logs optional: disable logs if True
|
643
|
+
#
|
644
|
+
# @return[Boolean]
|
645
|
+
def check_presegmentation(campaign, user_id, custom_variables, api_name, disable_logs = false)
|
646
|
+
campaign_key = campaign['key']
|
647
|
+
segments = get_segments(campaign)
|
648
|
+
is_valid_segments = valid_value?(segments)
|
649
|
+
|
650
|
+
if is_valid_segments
|
651
|
+
unless custom_variables
|
652
|
+
@logger.log(
|
653
|
+
LogLevelEnum::INFO,
|
654
|
+
format(
|
655
|
+
LogMessageEnum::InfoMessages::NO_CUSTOM_VARIABLES,
|
656
|
+
file: FILE,
|
657
|
+
campaign_key: campaign_key,
|
658
|
+
user_id: user_id,
|
659
|
+
api_name: api_name
|
660
|
+
),
|
661
|
+
disable_logs
|
662
|
+
)
|
663
|
+
custom_variables = {}
|
664
|
+
end
|
665
|
+
unless @segment_evaluator.evaluate(campaign_key, user_id, segments, custom_variables, disable_logs)
|
666
|
+
@logger.log(
|
667
|
+
LogLevelEnum::INFO,
|
668
|
+
format(
|
669
|
+
LogMessageEnum::InfoMessages::USER_FAILED_SEGMENTATION,
|
670
|
+
file: FileNameEnum::SegmentEvaluator,
|
671
|
+
user_id: user_id,
|
672
|
+
campaign_key: campaign_key,
|
673
|
+
custom_variables: custom_variables
|
674
|
+
),
|
675
|
+
disable_logs
|
676
|
+
)
|
677
|
+
return false
|
678
|
+
end
|
679
|
+
@logger.log(
|
680
|
+
LogLevelEnum::INFO,
|
681
|
+
format(
|
682
|
+
LogMessageEnum::InfoMessages::USER_PASSED_SEGMENTATION,
|
683
|
+
file: FileNameEnum::SegmentEvaluator,
|
684
|
+
user_id: user_id,
|
685
|
+
campaign_key: campaign_key,
|
686
|
+
custom_variables: custom_variables
|
687
|
+
),
|
688
|
+
disable_logs
|
689
|
+
)
|
690
|
+
else
|
691
|
+
@logger.log(
|
692
|
+
LogLevelEnum::INFO,
|
693
|
+
format(
|
694
|
+
LogMessageEnum::InfoMessages::SKIPPING_SEGMENTATION,
|
695
|
+
file: FILE,
|
696
|
+
campaign_key: campaign_key,
|
697
|
+
user_id: user_id,
|
698
|
+
api_name: api_name,
|
699
|
+
variation: ''
|
700
|
+
),
|
701
|
+
disable_logs
|
702
|
+
)
|
703
|
+
end
|
704
|
+
true
|
705
|
+
end
|
706
|
+
|
707
|
+
# Finds and returns eligible campaigns from group_campaigns.
|
708
|
+
#
|
709
|
+
# @param[String] :user_id The unique key assigned to User
|
710
|
+
# @param[Hash] :called_campaign campaign for which api is called
|
711
|
+
# @param[Array] :group_campaigns campaigns part of group
|
712
|
+
# @param[String] :custom_variables Key/value pair for segmentation
|
713
|
+
#
|
714
|
+
# @return[Array]
|
715
|
+
def get_eligible_campaigns(user_id, group_campaigns, called_campaign, custom_variables)
|
716
|
+
eligible_campaigns = []
|
717
|
+
|
718
|
+
group_campaigns.each do |campaign|
|
719
|
+
if called_campaign["id"] == campaign["id"] || check_presegmentation(campaign, user_id, custom_variables, '', true) && @bucketer.user_part_of_campaign?(user_id, campaign)
|
720
|
+
eligible_campaigns.append(campaign)
|
721
|
+
end
|
722
|
+
end
|
723
|
+
return eligible_campaigns
|
724
|
+
end
|
725
|
+
|
726
|
+
# Finds and returns the winner campaign from eligible_campaigns list.
|
727
|
+
#
|
728
|
+
# @param[String] :user_id The unique key assigned to User
|
729
|
+
# @param[Array] :eligible_campaigns campaigns part of group which were eligible to be winner
|
730
|
+
#
|
731
|
+
# @return[Hash]
|
732
|
+
def get_winner_campaign(user_id, eligible_campaigns, group_id)
|
733
|
+
if eligible_campaigns.length == 1
|
734
|
+
return eligible_campaigns[0]
|
735
|
+
end
|
736
|
+
|
737
|
+
eligible_campaigns = scale_campaigns_weight(eligible_campaigns)
|
738
|
+
eligible_campaigns = set_campaign_allocation(eligible_campaigns)
|
739
|
+
bucket_value = @bucketer.get_bucket_value_for_user(user_id, {}, group_id, true)
|
740
|
+
return @bucketer.get_campaign_using_range(bucket_value, eligible_campaigns)
|
741
|
+
end
|
742
|
+
|
743
|
+
# Get campaign keys of all eligible Campaigns.
|
744
|
+
#
|
745
|
+
# @param[Array] :eligible_campaigns campaigns part of group which were eligible to be winner
|
746
|
+
#
|
747
|
+
# @return[Array]
|
748
|
+
def get_eligible_campaigns_key(eligible_campaigns)
|
749
|
+
eligible_campaigns_key = []
|
750
|
+
eligible_campaigns.each do |campaign|
|
751
|
+
eligible_campaigns_key.append(campaign["key"])
|
752
|
+
end
|
753
|
+
eligible_campaigns_key
|
754
|
+
end
|
755
|
+
|
756
|
+
# Get campaign keys of all non eligible Campaigns.
|
757
|
+
#
|
758
|
+
# @param[Array] :eligible_campaigns campaigns part of group which were eligible to be winner
|
759
|
+
# @param[Array] :group_campaigns campaigns part of group
|
760
|
+
#
|
761
|
+
# @return[Array]
|
762
|
+
def get_non_eligible_campaigns_key(eligible_campaigns, group_campaigns)
|
763
|
+
non_eligible_campaigns_key = []
|
764
|
+
group_campaigns.each do |campaign|
|
765
|
+
unless eligible_campaigns.include? campaign
|
766
|
+
non_eligible_campaigns_key.append(campaign["key"])
|
767
|
+
end
|
768
|
+
end
|
769
|
+
non_eligible_campaigns_key
|
770
|
+
end
|
771
|
+
|
772
|
+
# Checks if any other campaign in groupCampaigns satisfies whitelisting or is in user storage.
|
773
|
+
#
|
774
|
+
# @param[String] :user_id the unique ID assigned to User
|
775
|
+
# @param[Hash] :called_campaign campaign for which api is called
|
776
|
+
# @param[Array] :group_campaigns campaigns part of group
|
777
|
+
# @param[String] :group_name group name
|
778
|
+
# @param[Hash] :variation_targeting_variables Key/value pair of Whitelisting Custom Attributes
|
779
|
+
# @param[Boolean] :disable_logs optional: disable logs if True
|
780
|
+
# @return[Boolean]
|
781
|
+
|
782
|
+
def check_whitelisting_or_storage_for_grouped_campaigns(user_id, called_campaign, group_campaigns, group_name, variation_targeting_variables, disable_logs = false)
|
783
|
+
group_campaigns.each do |campaign|
|
784
|
+
if called_campaign["id"] != campaign["id"]
|
785
|
+
targeted_variation = evaluate_whitelisting(
|
786
|
+
user_id,
|
787
|
+
campaign,
|
788
|
+
'',
|
789
|
+
campaign["key"],
|
790
|
+
variation_targeting_variables,
|
791
|
+
true
|
792
|
+
)
|
793
|
+
if targeted_variation
|
794
|
+
@logger.log(
|
795
|
+
LogLevelEnum::INFO,
|
796
|
+
format(
|
797
|
+
LogMessageEnum::InfoMessages::OTHER_CAMPAIGN_SATISFIES_WHITELISTING_OR_STORAGE,
|
798
|
+
file: FILE,
|
799
|
+
campaign_key: campaign["key"],
|
800
|
+
user_id: user_id,
|
801
|
+
group_name: group_name,
|
802
|
+
type: "whitelisting"
|
803
|
+
),
|
804
|
+
disable_logs
|
805
|
+
)
|
806
|
+
return true
|
807
|
+
end
|
808
|
+
end
|
809
|
+
end
|
810
|
+
|
811
|
+
group_campaigns.each do |campaign|
|
812
|
+
if called_campaign["id"] != campaign["id"]
|
813
|
+
user_storage_data = get_user_storage(user_id, campaign["key"], true)
|
814
|
+
if user_storage_data
|
815
|
+
@logger.log(
|
816
|
+
LogLevelEnum::INFO,
|
817
|
+
format(
|
818
|
+
LogMessageEnum::InfoMessages::OTHER_CAMPAIGN_SATISFIES_WHITELISTING_OR_STORAGE,
|
819
|
+
file: FILE,
|
820
|
+
campaign_key: campaign["key"],
|
821
|
+
user_id: user_id,
|
822
|
+
group_name: group_name,
|
823
|
+
type: "user storage"
|
824
|
+
),
|
825
|
+
disable_logs
|
826
|
+
)
|
827
|
+
return true
|
828
|
+
end
|
829
|
+
end
|
830
|
+
end
|
831
|
+
false
|
832
|
+
end
|
833
|
+
|
834
|
+
# Get variation if whitelisting passes
|
835
|
+
#
|
836
|
+
# @param[String] :user_id the unique ID assigned to User
|
837
|
+
# @param[Hash] :campaign campaign for which checking whitelisting
|
838
|
+
# @param[Hash] :variation_targeting_variables Key/value pair of Whitelisting Custom Attributes
|
839
|
+
# @param[String] :api_name The key Passed to identify the calling API
|
840
|
+
# @param[Hash] :decision data containing campaign info passed to hooks manager
|
841
|
+
# @param[Boolean] :disable_logs optional: disable logs if True
|
842
|
+
# @return[Hash]
|
843
|
+
def get_variation_if_whitelisting_passed(user_id, campaign, variation_targeting_variables, api_name, decision, disable_logs = false)
|
844
|
+
campaign_key = campaign['key']
|
845
|
+
if campaign['isForcedVariationEnabled']
|
846
|
+
variation = evaluate_whitelisting(
|
847
|
+
user_id,
|
848
|
+
campaign,
|
849
|
+
api_name,
|
850
|
+
campaign_key,
|
851
|
+
variation_targeting_variables,
|
852
|
+
disable_logs
|
853
|
+
)
|
854
|
+
status = if variation
|
855
|
+
StatusEnum::PASSED
|
856
|
+
else
|
857
|
+
StatusEnum::FAILED
|
858
|
+
end
|
859
|
+
|
860
|
+
@logger.log(
|
861
|
+
LogLevelEnum::INFO,
|
862
|
+
format(
|
863
|
+
LogMessageEnum::InfoMessages::SEGMENTATION_STATUS,
|
864
|
+
file: FILE,
|
865
|
+
campaign_key: campaign_key,
|
866
|
+
user_id: user_id,
|
867
|
+
status: status,
|
868
|
+
custom_variables: variation_targeting_variables,
|
869
|
+
variation_name: status == StatusEnum::PASSED ? "and #{variation['name']} is Assigned" : ' ',
|
870
|
+
segmentation_type: SegmentationTypeEnum::WHITELISTING,
|
871
|
+
api_name: api_name
|
872
|
+
),
|
873
|
+
disable_logs
|
874
|
+
)
|
875
|
+
|
876
|
+
if variation
|
877
|
+
if campaign['type'] == CampaignTypes::VISUAL_AB || campaign['type'] == CampaignTypes::FEATURE_TEST
|
878
|
+
decision[:variation_name] = variation['name']
|
879
|
+
decision[:variation_id] = variation['id']
|
880
|
+
if campaign['type'] == CampaignTypes::FEATURE_TEST
|
881
|
+
decision[:is_feature_enabled] = variation['isFeatureEnabled']
|
882
|
+
elsif campaign['type'] == CampaignTypes::VISUAL_AB
|
883
|
+
decision[:is_user_whitelisted] = !!variation['name']
|
884
|
+
end
|
885
|
+
end
|
886
|
+
@hooks_manager.execute(decision)
|
887
|
+
end
|
888
|
+
|
889
|
+
return variation if variation && variation['name']
|
890
|
+
else
|
891
|
+
@logger.log(
|
892
|
+
LogLevelEnum::INFO,
|
893
|
+
format(
|
894
|
+
LogMessageEnum::InfoMessages::WHITELISTING_SKIPPED,
|
895
|
+
file: FILE,
|
896
|
+
campaign_key: campaign_key,
|
897
|
+
user_id: user_id,
|
898
|
+
api_name: api_name
|
899
|
+
),
|
900
|
+
disable_logs
|
901
|
+
)
|
902
|
+
end
|
903
|
+
nil
|
904
|
+
end
|
905
|
+
|
617
906
|
end
|
618
907
|
end
|
619
908
|
end
|