1- # Copyright 2017-2019 , Optimizely
1+ # Copyright 2017-2020 , Optimizely
22# Licensed under the Apache License, Version 2.0 (the "License");
33# you may not use this file except in compliance with the License.
44# You may obtain a copy of the License at
2121from .helpers import validator
2222from .user_profile import UserProfile
2323
24+
2425Decision = namedtuple ('Decision' , 'experiment variation source' )
2526
2627
@@ -250,7 +251,7 @@ def get_variation(self, project_config, experiment, user_id, attributes, ignore_
250251 try :
251252 retrieved_profile = self .user_profile_service .lookup (user_id )
252253 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 ) )
254255 retrieved_profile = None
255256
256257 if validator .is_user_profile_valid (retrieved_profile ):
@@ -262,24 +263,33 @@ def get_variation(self, project_config, experiment, user_id, attributes, ignore_
262263 self .logger .warning ('User profile has invalid format.' )
263264
264265 # 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 ))
267273 return None
268274
269275 # Determine bucketing ID to be used
270276 bucketing_id = self ._get_bucketing_id (user_id , attributes )
271277 variation = self .bucketer .bucket (project_config , experiment , user_id , bucketing_id )
272278
273279 if variation :
280+ self .logger .info (
281+ 'User "%s" is in variation "%s" of experiment %s.' % (user_id , variation .key , experiment .key )
282+ )
274283 # Store this new decision and return the variation for the user
275284 if not ignore_user_profile and self .user_profile_service :
276285 try :
277286 user_profile .save_variation_for_experiment (experiment .id , variation .id )
278287 self .user_profile_service .save (user_profile .__dict__ )
279288 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 ) )
281290 return variation
282291
292+ self .logger .info ('User "%s" is in no variation.' % user_id )
283293 return None
284294
285295 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
299309 # Go through each experiment in order and try to get the variation for the user
300310 if rollout and len (rollout .experiments ) > 0 :
301311 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' ))
303314
304315 # 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 ))
307325 continue
308326
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 ))
310329 # Determine bucketing ID to be used
311330 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 )
313332 if variation :
314333 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 )
316335 )
317- return Decision (experiment , variation , enums .DecisionSources .ROLLOUT )
336+ return Decision (rollout_rule , variation , enums .DecisionSources .ROLLOUT )
318337 else :
319338 # Evaluate no further rules
320339 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 )
323342 )
324343 break
325344
326345 # 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 (
329349 project_config ,
330- project_config .get_experiment_from_key (rollout .experiments [- 1 ].get ('key' )),
350+ audience_conditions ,
351+ enums .RolloutRuleAudienceEvaluationLogs ,
352+ 'Everyone Else' ,
331353 attributes ,
332- self .logger ,
354+ self .logger
333355 ):
334356 # Determine bucketing ID to be used
335357 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 )
337359 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 ,)
340362
341363 return Decision (None , None , enums .DecisionSources .ROLLOUT )
342364
@@ -392,9 +414,6 @@ def get_variation_for_feature(self, project_config, feature, user_id, attributes
392414 variation = self .get_variation (project_config , experiment , user_id , attributes )
393415
394416 if variation :
395- self .logger .debug (
396- 'User "%s" is in variation %s of experiment %s.' % (user_id , variation .key , experiment .key )
397- )
398417 return Decision (experiment , variation , enums .DecisionSources .FEATURE_TEST )
399418 else :
400419 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
407426 variation = self .get_variation (project_config , experiment , user_id , attributes )
408427
409428 if variation :
410- self .logger .debug (
411- 'User "%s" is in variation %s of experiment %s.' % (user_id , variation .key , experiment .key )
412- )
413429 return Decision (experiment , variation , enums .DecisionSources .FEATURE_TEST )
414430
415431 # Next check if user is part of a rollout
0 commit comments