@@ -32,6 +32,12 @@ def __init__(self, logger, user_profile_service):
32
32
self .logger = logger
33
33
self .user_profile_service = user_profile_service
34
34
35
+ # Map of user IDs to another map of experiments to variations.
36
+ # This contains all the forced variations set by the user
37
+ # by calling set_forced_variation (it is not the same as the
38
+ # whitelisting forcedVariations data structure).
39
+ self .forced_variation_map = {}
40
+
35
41
def _get_bucketing_id (self , user_id , attributes ):
36
42
""" Helper method to determine bucketing ID for the user.
37
43
@@ -54,8 +60,114 @@ def _get_bucketing_id(self, user_id, attributes):
54
60
55
61
return user_id
56
62
57
- def get_forced_variation (self , project_config , experiment , user_id ):
58
- """ Determine if a user is forced into a variation for the given experiment and return that variation.
63
+ def set_forced_variation (self , project_config , experiment_key , user_id , variation_key ):
64
+ """ Sets users to a map of experiments to forced variations.
65
+
66
+ Args:
67
+ project_config: Instance of ProjectConfig.
68
+ experiment_key: Key for experiment.
69
+ user_id: The user ID.
70
+ variation_key: Key for variation. If None, then clear the existing experiment-to-variation mapping.
71
+
72
+ Returns:
73
+ A boolean value that indicates if the set completed successfully.
74
+ """
75
+ experiment = project_config .get_experiment_from_key (experiment_key )
76
+ if not experiment :
77
+ # The invalid experiment key will be logged inside this call.
78
+ return False
79
+
80
+ experiment_id = experiment .id
81
+ if variation_key is None :
82
+ if user_id in self .forced_variation_map :
83
+ experiment_to_variation_map = self .forced_variation_map .get (user_id )
84
+ if experiment_id in experiment_to_variation_map :
85
+ del (self .forced_variation_map [user_id ][experiment_id ])
86
+ self .logger .debug ('Variation mapped to experiment "%s" has been removed for user "%s".' % (
87
+ experiment_key ,
88
+ user_id
89
+ ))
90
+ else :
91
+ self .logger .debug ('Nothing to remove. Variation mapped to experiment "%s" for user "%s" does not exist.' % (
92
+ experiment_key ,
93
+ user_id
94
+ ))
95
+ else :
96
+ self .logger .debug ('Nothing to remove. User "%s" does not exist in the forced variation map.' % user_id )
97
+ return True
98
+
99
+ if not validator .is_non_empty_string (variation_key ):
100
+ self .logger .debug ('Variation key is invalid.' )
101
+ return False
102
+
103
+ forced_variation = project_config .get_variation_from_key (experiment_key , variation_key )
104
+ if not forced_variation :
105
+ # The invalid variation key will be logged inside this call.
106
+ return False
107
+
108
+ variation_id = forced_variation .id
109
+
110
+ if user_id not in self .forced_variation_map :
111
+ self .forced_variation_map [user_id ] = {experiment_id : variation_id }
112
+ else :
113
+ self .forced_variation_map [user_id ][experiment_id ] = variation_id
114
+
115
+ self .logger .debug ('Set variation "%s" for experiment "%s" and user "%s" in the forced variation map.' % (
116
+ variation_id ,
117
+ experiment_id ,
118
+ user_id
119
+ ))
120
+ return True
121
+
122
+ def get_forced_variation (self , project_config , experiment_key , user_id ):
123
+ """ Gets the forced variation key for the given user and experiment.
124
+
125
+ Args:
126
+ project_config: Instance of ProjectConfig.
127
+ experiment_key: Key for experiment.
128
+ user_id: The user ID.
129
+
130
+ Returns:
131
+ The variation which the given user and experiment should be forced into.
132
+ """
133
+
134
+ if user_id not in self .forced_variation_map :
135
+ self .logger .debug ('User "%s" is not in the forced variation map.' % user_id )
136
+ return None
137
+
138
+ experiment = project_config .get_experiment_from_key (experiment_key )
139
+ if not experiment :
140
+ # The invalid experiment key will be logged inside this call.
141
+ return None
142
+
143
+ experiment_to_variation_map = self .forced_variation_map .get (user_id )
144
+
145
+ if not experiment_to_variation_map :
146
+ self .logger .debug ('No experiment "%s" mapped to user "%s" in the forced variation map.' % (
147
+ experiment_key ,
148
+ user_id
149
+ ))
150
+ return None
151
+
152
+ variation_id = experiment_to_variation_map .get (experiment .id )
153
+ if variation_id is None :
154
+ self .logger .debug (
155
+ 'No variation mapped to experiment "%s" in the forced variation map.' % experiment_key
156
+ )
157
+ return None
158
+
159
+ variation = project_config .get_variation_from_id (experiment_key , variation_id )
160
+
161
+ self .logger .debug ('Variation "%s" is mapped to experiment "%s" and user "%s" in the forced variation map' % (
162
+ variation .key ,
163
+ experiment_key ,
164
+ user_id
165
+ ))
166
+ return variation
167
+
168
+ def get_whitelisted_variation (self , project_config , experiment , user_id ):
169
+ """ Determine if a user is forced into a variation (through whitelisting)
170
+ for the given experiment and return that variation.
59
171
60
172
Args:
61
173
project_config: Instance of ProjectConfig.
@@ -129,12 +241,12 @@ def get_variation(self, project_config, experiment, user_id, attributes, ignore_
129
241
return None
130
242
131
243
# Check if the user is forced into a variation
132
- variation = project_config .get_forced_variation (experiment .key , user_id )
244
+ variation = self .get_forced_variation (project_config , experiment .key , user_id )
133
245
if variation :
134
246
return variation
135
247
136
248
# Check to see if user is white-listed for a certain variation
137
- variation = self .get_forced_variation (project_config , experiment , user_id )
249
+ variation = self .get_whitelisted_variation (project_config , experiment , user_id )
138
250
if variation :
139
251
return variation
140
252
0 commit comments