2727class DecisionService (object ):
2828 """ Class encapsulating all decision related capabilities. """
2929
30- def __init__ (self , config , user_profile_service ):
31- self .bucketer = bucketer .Bucketer (config )
30+ def __init__ (self , logger , user_profile_service ):
31+ self .bucketer = bucketer .Bucketer ()
32+ self .logger = logger
3233 self .user_profile_service = user_profile_service
33- self .config = config
34- self .logger = config .logger
3534
3635 def _get_bucketing_id (self , user_id , attributes ):
3736 """ Helper method to determine bucketing ID for the user.
@@ -55,10 +54,11 @@ def _get_bucketing_id(self, user_id, attributes):
5554
5655 return user_id
5756
58- def get_forced_variation (self , experiment , user_id ):
57+ def get_forced_variation (self , project_config , experiment , user_id ):
5958 """ Determine if a user is forced into a variation for the given experiment and return that variation.
6059
6160 Args:
61+ project_config: Instance of ProjectConfig.
6262 experiment: Object representing the experiment for which user is to be bucketed.
6363 user_id: ID for the user.
6464
@@ -69,17 +69,18 @@ def get_forced_variation(self, experiment, user_id):
6969 forced_variations = experiment .forcedVariations
7070 if forced_variations and user_id in forced_variations :
7171 variation_key = forced_variations .get (user_id )
72- variation = self . config .get_variation_from_key (experiment .key , variation_key )
72+ variation = project_config .get_variation_from_key (experiment .key , variation_key )
7373 if variation :
7474 self .logger .info ('User "%s" is forced in variation "%s".' % (user_id , variation_key ))
7575 return variation
7676
7777 return None
7878
79- def get_stored_variation (self , experiment , user_profile ):
79+ def get_stored_variation (self , project_config , experiment , user_profile ):
8080 """ Determine if the user has a stored variation available for the given experiment and return that.
8181
8282 Args:
83+ project_config: Instance of ProjectConfig.
8384 experiment: Object representing the experiment for which user is to be bucketed.
8485 user_profile: UserProfile object representing the user's profile.
8586
@@ -91,7 +92,7 @@ def get_stored_variation(self, experiment, user_profile):
9192 variation_id = user_profile .get_variation_for_experiment (experiment .id )
9293
9394 if variation_id :
94- variation = self . config .get_variation_from_id (experiment .key , variation_id )
95+ variation = project_config .get_variation_from_id (experiment .key , variation_id )
9596 if variation :
9697 self .logger .info ('Found a stored decision. User "%s" is in variation "%s" of experiment "%s".' % (
9798 user_id ,
@@ -102,7 +103,7 @@ def get_stored_variation(self, experiment, user_profile):
102103
103104 return None
104105
105- def get_variation (self , experiment , user_id , attributes , ignore_user_profile = False ):
106+ def get_variation (self , project_config , experiment , user_id , attributes , ignore_user_profile = False ):
106107 """ Top-level function to help determine variation user should be put in.
107108
108109 First, check if experiment is running.
@@ -112,6 +113,7 @@ def get_variation(self, experiment, user_id, attributes, ignore_user_profile=Fal
112113 Fifth, bucket the user and return the variation.
113114
114115 Args:
116+ project_config: Instance of ProjectConfig.
115117 experiment: Experiment for which user variation needs to be determined.
116118 user_id: ID for user.
117119 attributes: Dict representing user attributes.
@@ -127,12 +129,12 @@ def get_variation(self, experiment, user_id, attributes, ignore_user_profile=Fal
127129 return None
128130
129131 # Check if the user is forced into a variation
130- variation = self . config .get_forced_variation (experiment .key , user_id )
132+ variation = project_config .get_forced_variation (experiment .key , user_id )
131133 if variation :
132134 return variation
133135
134136 # Check to see if user is white-listed for a certain variation
135- variation = self .get_forced_variation (experiment , user_id )
137+ variation = self .get_forced_variation (project_config , experiment , user_id )
136138 if variation :
137139 return variation
138140
@@ -147,14 +149,14 @@ def get_variation(self, experiment, user_id, attributes, ignore_user_profile=Fal
147149
148150 if validator .is_user_profile_valid (retrieved_profile ):
149151 user_profile = UserProfile (** retrieved_profile )
150- variation = self .get_stored_variation (experiment , user_profile )
152+ variation = self .get_stored_variation (project_config , experiment , user_profile )
151153 if variation :
152154 return variation
153155 else :
154156 self .logger .warning ('User profile has invalid format.' )
155157
156158 # Bucket user and store the new decision
157- if not audience_helper .is_user_in_experiment (self . config , experiment , attributes , self .logger ):
159+ if not audience_helper .is_user_in_experiment (project_config , experiment , attributes , self .logger ):
158160 self .logger .info ('User "%s" does not meet conditions to be in experiment "%s".' % (
159161 user_id ,
160162 experiment .key
@@ -163,7 +165,7 @@ def get_variation(self, experiment, user_id, attributes, ignore_user_profile=Fal
163165
164166 # Determine bucketing ID to be used
165167 bucketing_id = self ._get_bucketing_id (user_id , attributes )
166- variation = self .bucketer .bucket (experiment , user_id , bucketing_id )
168+ variation = self .bucketer .bucket (project_config , experiment , user_id , bucketing_id )
167169
168170 if variation :
169171 # Store this new decision and return the variation for the user
@@ -177,11 +179,12 @@ def get_variation(self, experiment, user_id, attributes, ignore_user_profile=Fal
177179
178180 return None
179181
180- def get_variation_for_rollout (self , rollout , user_id , attributes = None ):
182+ def get_variation_for_rollout (self , project_config , rollout , user_id , attributes = None ):
181183 """ Determine which experiment/variation the user is in for a given rollout.
182184 Returns the variation of the first experiment the user qualifies for.
183185
184186 Args:
187+ project_config: Instance of ProjectConfig.
185188 rollout: Rollout for which we are getting the variation.
186189 user_id: ID for user.
187190 attributes: Dict representing user attributes.
@@ -193,10 +196,10 @@ def get_variation_for_rollout(self, rollout, user_id, attributes=None):
193196 # Go through each experiment in order and try to get the variation for the user
194197 if rollout and len (rollout .experiments ) > 0 :
195198 for idx in range (len (rollout .experiments ) - 1 ):
196- experiment = self . config .get_experiment_from_key (rollout .experiments [idx ].get ('key' ))
199+ experiment = project_config .get_experiment_from_key (rollout .experiments [idx ].get ('key' ))
197200
198201 # Check if user meets audience conditions for targeting rule
199- if not audience_helper .is_user_in_experiment (self . config , experiment , attributes , self .logger ):
202+ if not audience_helper .is_user_in_experiment (project_config , experiment , attributes , self .logger ):
200203 self .logger .debug ('User "%s" does not meet conditions for targeting rule %s.' % (
201204 user_id ,
202205 idx + 1
@@ -206,7 +209,7 @@ def get_variation_for_rollout(self, rollout, user_id, attributes=None):
206209 self .logger .debug ('User "%s" meets conditions for targeting rule %s.' % (user_id , idx + 1 ))
207210 # Determine bucketing ID to be used
208211 bucketing_id = self ._get_bucketing_id (user_id , attributes )
209- variation = self .bucketer .bucket (experiment , user_id , bucketing_id )
212+ variation = self .bucketer .bucket (project_config , experiment , user_id , bucketing_id )
210213 if variation :
211214 self .logger .debug ('User "%s" is in variation %s of experiment %s.' % (
212215 user_id ,
@@ -221,34 +224,36 @@ def get_variation_for_rollout(self, rollout, user_id, attributes=None):
221224 break
222225
223226 # Evaluate last rule i.e. "Everyone Else" rule
224- everyone_else_experiment = self .config .get_experiment_from_key (rollout .experiments [- 1 ].get ('key' ))
225- if audience_helper .is_user_in_experiment (self .config ,
226- self .config .get_experiment_from_key (rollout .experiments [- 1 ].get ('key' )),
227- attributes ,
228- self .logger ):
227+ everyone_else_experiment = project_config .get_experiment_from_key (rollout .experiments [- 1 ].get ('key' ))
228+ if audience_helper .is_user_in_experiment (
229+ project_config ,
230+ project_config .get_experiment_from_key (rollout .experiments [- 1 ].get ('key' )),
231+ attributes ,
232+ self .logger ):
229233 # Determine bucketing ID to be used
230234 bucketing_id = self ._get_bucketing_id (user_id , attributes )
231- variation = self .bucketer .bucket (everyone_else_experiment , user_id , bucketing_id )
235+ variation = self .bucketer .bucket (project_config , everyone_else_experiment , user_id , bucketing_id )
232236 if variation :
233237 self .logger .debug ('User "%s" meets conditions for targeting rule "Everyone Else".' % user_id )
234238 return Decision (everyone_else_experiment , variation , enums .DecisionSources .ROLLOUT )
235239
236240 return Decision (None , None , enums .DecisionSources .ROLLOUT )
237241
238- def get_experiment_in_group (self , group , bucketing_id ):
242+ def get_experiment_in_group (self , project_config , group , bucketing_id ):
239243 """ Determine which experiment in the group the user is bucketed into.
240244
241245 Args:
246+ project_config: Instance of ProjectConfig.
242247 group: The group to bucket the user into.
243248 bucketing_id: ID to be used for bucketing the user.
244249
245250 Returns:
246251 Experiment if the user is bucketed into an experiment in the specified group. None otherwise.
247252 """
248253
249- experiment_id = self .bucketer .find_bucket (bucketing_id , group .id , group .trafficAllocation )
254+ experiment_id = self .bucketer .find_bucket (project_config , bucketing_id , group .id , group .trafficAllocation )
250255 if experiment_id :
251- experiment = self . config .get_experiment_from_id (experiment_id )
256+ experiment = project_config .get_experiment_from_id (experiment_id )
252257 if experiment :
253258 self .logger .info ('User with bucketing ID "%s" is in experiment %s of group %s.' % (
254259 bucketing_id ,
@@ -264,10 +269,11 @@ def get_experiment_in_group(self, group, bucketing_id):
264269
265270 return None
266271
267- def get_variation_for_feature (self , feature , user_id , attributes = None ):
272+ def get_variation_for_feature (self , project_config , feature , user_id , attributes = None ):
268273 """ Returns the experiment/variation the user is bucketed in for the given feature.
269274
270275 Args:
276+ project_config: Instance of ProjectConfig.
271277 feature: Feature for which we are determining if it is enabled or not for the given user.
272278 user_id: ID for user.
273279 attributes: Dict representing user attributes.
@@ -276,17 +282,15 @@ def get_variation_for_feature(self, feature, user_id, attributes=None):
276282 Decision namedtuple consisting of experiment and variation for the user.
277283 """
278284
279- experiment = None
280- variation = None
281285 bucketing_id = self ._get_bucketing_id (user_id , attributes )
282286
283287 # First check if the feature is in a mutex group
284288 if feature .groupId :
285- group = self . config .get_group (feature .groupId )
289+ group = project_config .get_group (feature .groupId )
286290 if group :
287- experiment = self .get_experiment_in_group (group , bucketing_id )
291+ experiment = self .get_experiment_in_group (project_config , group , bucketing_id )
288292 if experiment and experiment .id in feature .experimentIds :
289- variation = self .get_variation (experiment , user_id , attributes )
293+ variation = self .get_variation (project_config , experiment , user_id , attributes )
290294
291295 if variation :
292296 self .logger .debug ('User "%s" is in variation %s of experiment %s.' % (
@@ -301,9 +305,9 @@ def get_variation_for_feature(self, feature, user_id, attributes=None):
301305 # Next check if the feature is being experimented on
302306 elif feature .experimentIds :
303307 # If an experiment is not in a group, then the feature can only be associated with one experiment
304- experiment = self . config .get_experiment_from_id (feature .experimentIds [0 ])
308+ experiment = project_config .get_experiment_from_id (feature .experimentIds [0 ])
305309 if experiment :
306- variation = self .get_variation (experiment , user_id , attributes )
310+ variation = self .get_variation (project_config , experiment , user_id , attributes )
307311
308312 if variation :
309313 self .logger .debug ('User "%s" is in variation %s of experiment %s.' % (
@@ -315,7 +319,7 @@ def get_variation_for_feature(self, feature, user_id, attributes=None):
315319
316320 # Next check if user is part of a rollout
317321 if feature .rolloutId :
318- rollout = self . config .get_rollout_from_id (feature .rolloutId )
319- return self .get_variation_for_rollout (rollout , user_id , attributes )
322+ rollout = project_config .get_rollout_from_id (feature .rolloutId )
323+ return self .get_variation_for_rollout (project_config , rollout , user_id , attributes )
320324 else :
321325 return Decision (None , None , enums .DecisionSources .ROLLOUT )
0 commit comments