1
- # Copyright 2017-2019 , Optimizely
1
+ # Copyright 2017-2020 , Optimizely
2
2
# Licensed under the Apache License, Version 2.0 (the "License");
3
3
# you may not use this file except in compliance with the License.
4
4
# You may obtain a copy of the License at
21
21
from .helpers import validator
22
22
from .user_profile import UserProfile
23
23
24
+
24
25
Decision = namedtuple ('Decision' , 'experiment variation source' )
25
26
26
27
@@ -250,7 +251,7 @@ def get_variation(self, project_config, experiment, user_id, attributes, ignore_
250
251
try :
251
252
retrieved_profile = self .user_profile_service .lookup (user_id )
252
253
except :
253
- self .logger .exception ('Unable to retrieve user profile for user "%s " as lookup failed.' % user_id )
254
+ self .logger .exception ('Unable to retrieve user profile for user "{} " as lookup failed.' . format ( user_id ) )
254
255
retrieved_profile = None
255
256
256
257
if validator .is_user_profile_valid (retrieved_profile ):
@@ -262,24 +263,33 @@ def get_variation(self, project_config, experiment, user_id, attributes, ignore_
262
263
self .logger .warning ('User profile has invalid format.' )
263
264
264
265
# Bucket user and store the new decision
265
- if not audience_helper .is_user_in_experiment (project_config , experiment , attributes , self .logger ):
266
- self .logger .info ('User "%s" does not meet conditions to be in experiment "%s".' % (user_id , experiment .key ))
266
+ audience_conditions = experiment .get_audience_conditions_or_ids ()
267
+ if not audience_helper .does_user_meet_audience_conditions (project_config , audience_conditions ,
268
+ enums .ExperimentAudienceEvaluationLogs ,
269
+ experiment .key ,
270
+ attributes , self .logger ):
271
+ self .logger .info (
272
+ 'User "{}" does not meet conditions to be in experiment "{}".' .format (user_id , experiment .key ))
267
273
return None
268
274
269
275
# Determine bucketing ID to be used
270
276
bucketing_id = self ._get_bucketing_id (user_id , attributes )
271
277
variation = self .bucketer .bucket (project_config , experiment , user_id , bucketing_id )
272
278
273
279
if variation :
280
+ self .logger .info (
281
+ 'User "%s" is in variation "%s" of experiment %s.' % (user_id , variation .key , experiment .key )
282
+ )
274
283
# Store this new decision and return the variation for the user
275
284
if not ignore_user_profile and self .user_profile_service :
276
285
try :
277
286
user_profile .save_variation_for_experiment (experiment .id , variation .id )
278
287
self .user_profile_service .save (user_profile .__dict__ )
279
288
except :
280
- self .logger .exception ('Unable to save user profile for user "%s ".' % user_id )
289
+ self .logger .exception ('Unable to save user profile for user "{} ".' . format ( user_id ) )
281
290
return variation
282
291
292
+ self .logger .info ('User "%s" is in no variation.' % user_id )
283
293
return None
284
294
285
295
def get_variation_for_rollout (self , project_config , rollout , user_id , attributes = None ):
@@ -299,44 +309,56 @@ def get_variation_for_rollout(self, project_config, rollout, user_id, attributes
299
309
# Go through each experiment in order and try to get the variation for the user
300
310
if rollout and len (rollout .experiments ) > 0 :
301
311
for idx in range (len (rollout .experiments ) - 1 ):
302
- experiment = project_config .get_experiment_from_key (rollout .experiments [idx ].get ('key' ))
312
+ logging_key = str (idx + 1 )
313
+ rollout_rule = project_config .get_experiment_from_key (rollout .experiments [idx ].get ('key' ))
303
314
304
315
# Check if user meets audience conditions for targeting rule
305
- if not audience_helper .is_user_in_experiment (project_config , experiment , attributes , self .logger ):
306
- self .logger .debug ('User "%s" does not meet conditions for targeting rule %s.' % (user_id , idx + 1 ))
316
+ audience_conditions = rollout_rule .get_audience_conditions_or_ids ()
317
+ if not audience_helper .does_user_meet_audience_conditions (project_config ,
318
+ audience_conditions ,
319
+ enums .RolloutRuleAudienceEvaluationLogs ,
320
+ logging_key ,
321
+ attributes ,
322
+ self .logger ):
323
+ self .logger .debug (
324
+ 'User "{}" does not meet conditions for targeting rule {}.' .format (user_id , logging_key ))
307
325
continue
308
326
309
- self .logger .debug ('User "%s" meets conditions for targeting rule %s.' % (user_id , idx + 1 ))
327
+ self .logger .debug (
328
+ 'User "{}" meets audience conditions for targeting rule {}.' .format (user_id , idx + 1 ))
310
329
# Determine bucketing ID to be used
311
330
bucketing_id = self ._get_bucketing_id (user_id , attributes )
312
- variation = self .bucketer .bucket (project_config , experiment , user_id , bucketing_id )
331
+ variation = self .bucketer .bucket (project_config , rollout_rule , user_id , bucketing_id )
313
332
if variation :
314
333
self .logger .debug (
315
- 'User "%s " is in variation %s of experiment %s.' % (user_id , variation . key , experiment . key )
334
+ 'User "{} " is in the traffic group of targeting rule {}.' . format (user_id , logging_key )
316
335
)
317
- return Decision (experiment , variation , enums .DecisionSources .ROLLOUT )
336
+ return Decision (rollout_rule , variation , enums .DecisionSources .ROLLOUT )
318
337
else :
319
338
# Evaluate no further rules
320
339
self .logger .debug (
321
- 'User "%s " is not in the traffic group for the targeting else . '
322
- 'Checking "Everyone Else" rule now.' % user_id
340
+ 'User "{} " is not in the traffic group for targeting rule {} . '
341
+ 'Checking "Everyone Else" rule now.' . format ( user_id , logging_key )
323
342
)
324
343
break
325
344
326
345
# Evaluate last rule i.e. "Everyone Else" rule
327
- everyone_else_experiment = project_config .get_experiment_from_key (rollout .experiments [- 1 ].get ('key' ))
328
- if audience_helper .is_user_in_experiment (
346
+ everyone_else_rule = project_config .get_experiment_from_key (rollout .experiments [- 1 ].get ('key' ))
347
+ audience_conditions = everyone_else_rule .get_audience_conditions_or_ids ()
348
+ if audience_helper .does_user_meet_audience_conditions (
329
349
project_config ,
330
- project_config .get_experiment_from_key (rollout .experiments [- 1 ].get ('key' )),
350
+ audience_conditions ,
351
+ enums .RolloutRuleAudienceEvaluationLogs ,
352
+ 'Everyone Else' ,
331
353
attributes ,
332
- self .logger ,
354
+ self .logger
333
355
):
334
356
# Determine bucketing ID to be used
335
357
bucketing_id = self ._get_bucketing_id (user_id , attributes )
336
- variation = self .bucketer .bucket (project_config , everyone_else_experiment , user_id , bucketing_id )
358
+ variation = self .bucketer .bucket (project_config , everyone_else_rule , user_id , bucketing_id )
337
359
if variation :
338
- self .logger .debug ('User "%s " meets conditions for targeting rule "Everyone Else".' % user_id )
339
- return Decision (everyone_else_experiment , variation , enums .DecisionSources .ROLLOUT ,)
360
+ self .logger .debug ('User "{} " meets conditions for targeting rule "Everyone Else".' . format ( user_id ) )
361
+ return Decision (everyone_else_rule , variation , enums .DecisionSources .ROLLOUT ,)
340
362
341
363
return Decision (None , None , enums .DecisionSources .ROLLOUT )
342
364
@@ -392,9 +414,6 @@ def get_variation_for_feature(self, project_config, feature, user_id, attributes
392
414
variation = self .get_variation (project_config , experiment , user_id , attributes )
393
415
394
416
if variation :
395
- self .logger .debug (
396
- 'User "%s" is in variation %s of experiment %s.' % (user_id , variation .key , experiment .key )
397
- )
398
417
return Decision (experiment , variation , enums .DecisionSources .FEATURE_TEST )
399
418
else :
400
419
self .logger .error (enums .Errors .INVALID_GROUP_ID .format ('_get_variation_for_feature' ))
@@ -407,9 +426,6 @@ def get_variation_for_feature(self, project_config, feature, user_id, attributes
407
426
variation = self .get_variation (project_config , experiment , user_id , attributes )
408
427
409
428
if variation :
410
- self .logger .debug (
411
- 'User "%s" is in variation %s of experiment %s.' % (user_id , variation .key , experiment .key )
412
- )
413
429
return Decision (experiment , variation , enums .DecisionSources .FEATURE_TEST )
414
430
415
431
# Next check if user is part of a rollout
0 commit comments