14
14
#
15
15
16
16
import threading
17
+ import copy
17
18
18
19
19
20
class OptimizelyUserContext (object ):
20
21
"""
21
22
Representation of an Optimizely User Context using which APIs are to be called.
22
23
"""
24
+ forced_decisions = []
23
25
24
26
def __init__ (self , optimizely_client , user_id , user_attributes = None ):
25
27
""" Create an instance of the Optimizely User Context.
@@ -42,8 +44,39 @@ def __init__(self, optimizely_client, user_id, user_attributes=None):
42
44
self ._user_attributes = user_attributes .copy () if user_attributes else {}
43
45
self .lock = threading .Lock ()
44
46
47
+ # TODO - ADD FORCED DECISION class
48
+ """
49
+ struct ForcedDecision {
50
+ let flagKey: String
51
+ let ruleKey: String?
52
+ var variationKey: String
53
+ }
54
+ var forcedDecisions = AtomicArray<ForcedDecision>()
55
+ """
56
+ class ForcedDecision (object ):
57
+ def __init__ (self , flag_key , rule_key , variation_key ):
58
+ self .flag_key = flag_key
59
+ self .rule_key = rule_key
60
+ self .variation_key = variation_key
61
+
62
+
63
+ # TODO - NEW
45
64
def _clone (self ):
46
- return OptimizelyUserContext (self .client , self .user_id , self .get_user_attributes ())
65
+ if not self .client :
66
+ return None
67
+
68
+ user_context = OptimizelyUserContext (self .client , self .user_id , self .get_user_attributes ())
69
+
70
+ if len (self .forced_decisions ) > 0 :
71
+ # Jae:
72
+ # Make sure the assigning is to make a copy. Some langs use ref and other make a copy when assigning array/map.
73
+ # Swift makes a copy automatically when assigning value type. Not sure about python.
74
+ # So it needs to be pass by value. So the original object is not changed. Change is only in the new object. Here I’ll need to call copy.deepcopy()
75
+ # The opposite. We won’t change the contents of the copied one. But the original can be changed any time later.
76
+ user_context .forced_decisions = copy .deepcopy (self .forced_decisions ) # TODO - IMPORTANT -> CHECK IF WE NEED DEEPCOPY OR NOT - SEE SLACK W JAE
77
+
78
+ return user_context
79
+
47
80
48
81
def get_user_attributes (self ):
49
82
with self .lock :
@@ -114,3 +147,142 @@ def as_json(self):
114
147
'user_id' : self .user_id ,
115
148
'attributes' : self .get_user_attributes (),
116
149
}
150
+
151
+
152
+ # TODO - NEW
153
+ def set_forced_decision (self , flag_key , rule_key , variation_key ):
154
+ """
155
+ Sets the forced decision (variation key) for a given flag and an optional rule.
156
+
157
+ Args:
158
+ flag_key: A flag key.
159
+ rule_key: An experiment or delivery rule key (optional).
160
+ variation_key: A variation key.
161
+
162
+ Returns:
163
+ True if the forced decision has been set successfully.
164
+ """
165
+ config = self .client .get_optimizely_config ()
166
+
167
+ if self .client is None or config is None : # TODO - check if to use "is not" or "not =="
168
+ # TODO log error sdk key not ready - whichlogger, to show in console, logger for optimizely_client,loggger for what? where do we want it to log?
169
+
170
+ return False
171
+
172
+ if rule_key :
173
+ print ('xxx1 ' , self .forced_decisions )
174
+ for decision in self .forced_decisions :
175
+ if decision .flag_key == flag_key and decision .rule_key == rule_key :
176
+ decision .variation_key = variation_key # TODO check if .variation_key needs to be a dict key instead of dot notation object
177
+
178
+ self .forced_decisions .append (self .ForcedDecision (flag_key , rule_key , variation_key ))
179
+ print ('xxx2 ' , self .forced_decisions [0 ].variation_key )
180
+
181
+ return True
182
+
183
+
184
+ # TODO - NEW
185
+ def get_forced_decision (self , flag_key , rule_key ):
186
+ """
187
+ Sets the forced decision (variation key) for a given flag and an optional rule.
188
+
189
+ Args:
190
+ flag_key: A flag key.
191
+ rule_key: An experiment or delivery rule key (optional).
192
+
193
+ Returns:
194
+ A variation key or None if forced decisions are not set for the parameters.
195
+ """
196
+ config = self .client .get_optimizely_config ()
197
+
198
+ if self .client is None or config is None : # TODO - check if to use "is not" or "not =="
199
+ # TODO log error sdk key not ready - whichlogger, to sho win console, logger for optimizely_client,loggger for what? where do we want it to log?
200
+
201
+ return False
202
+
203
+ return self .find_forced_decision (flag_key , rule_key )
204
+
205
+
206
+ # TODO - NEW
207
+ def remove_forced_decision (self , flag_key , * arg ): # making rule_key here optional arg - WHAT ABOUT IF RULE_KEY IS KEYWORD ARG????? <--- CHECK THIS!
208
+ """
209
+ Removes the forced decision for a given flag and an optional rule.
210
+
211
+ Args:
212
+ flag_key: A flag key.
213
+ rule_key: An experiment or delivery rule key (optional).
214
+
215
+ Returns:
216
+ Returns: true if the forced decision has been removed successfully.
217
+ """
218
+ config = self .client .get_optimizely_config ()
219
+
220
+ if self .client is None or config is None : # TODO - check if to use "is not" or "not =="
221
+ # TODO log error sdk key not ready - whichlogger, to sho win console, logger for optimizely_client,loggger for what? where do we want it to log?
222
+
223
+ return False
224
+
225
+ # remove() built-in function by default removes the first occurrence of the element that meets the condition
226
+ for decision in self .forced_decisions :
227
+ if decision .flag_key == flag_key and decision .rule_key == arg :
228
+ self .forced_decisions .remove (decision ) #TODO - check if it needs to only remove the first occurrence and no other!!! Test separately if rmoe removes all occurences!
229
+
230
+ return False
231
+
232
+ # TODO - NEW
233
+ def remove_all_forced_decisions (self ):
234
+ """
235
+ Removes all forced decisions bound to this user context.
236
+
237
+ Returns:
238
+ True if forced decisions have been removed successfully.
239
+ """
240
+ config = self .client .get_optimizely_config ()
241
+
242
+ if self .client is None or config is None : # TODO - check if to use "is not" or "not =="
243
+ # TODO log error sdk key not ready - whichlogger, to sho win console, logger for optimizely_client,loggger for what? where do we want it to log?
244
+
245
+ return False
246
+
247
+ self .forced_decisions .clear ()
248
+
249
+ return True
250
+
251
+ # TODO - NEW
252
+ def find_forced_decision (self , flag_key , rule_key ):
253
+ if len (self .forced_decisions ) == 0 :
254
+ return None
255
+
256
+ for decision in self .forced_decisions :
257
+ if decision .flag_key == flag_key and decision .rule_key == rule_key :
258
+ return decision .variation_key
259
+
260
+
261
+
262
+ # TODO - For dding logger see this: https://github.com/optimizely/javascript-sdk/compare/pnguen/forced-decisions#diff-2bb39c11f271344df01b662f4313312870714813ceb8508ce7bdb851f09b5666R182-R192
263
+ # TODO - NEW
264
+ def find_validated_forced_decision (self , flag_key , rule_key , options ):
265
+ reasons = [] # TODO - what to do with reasons?? Jae has reasons. Do we need them?
266
+ variation_key = self .find_forced_decision (flag_key , rule_key )
267
+ if variation_key :
268
+ self .client .get_flag_variation_by_key (flag_key , variation_key )
269
+
270
+
271
+
272
+
273
+
274
+
275
+
276
+
277
+
278
+
279
+
280
+
281
+
282
+
283
+
284
+
285
+
286
+
287
+
288
+
0 commit comments