@@ -53,15 +53,20 @@ def __init__(self, datafile, logger, error_handler):
53
53
self .events = config .get ('events' , [])
54
54
self .attributes = config .get ('attributes' , [])
55
55
self .audiences = config .get ('audiences' , [])
56
- self .feature_flags = config .get ('variables' , [])
56
+ self .features = config .get ('features' , [])
57
+ self .layers = config .get ('layers' , [])
57
58
58
59
# Utility maps for quick lookup
59
60
self .group_id_map = self ._generate_key_map (self .groups , 'id' , entities .Group )
60
61
self .experiment_key_map = self ._generate_key_map (self .experiments , 'key' , entities .Experiment )
61
62
self .event_key_map = self ._generate_key_map (self .events , 'key' , entities .Event )
62
63
self .attribute_key_map = self ._generate_key_map (self .attributes , 'key' , entities .Attribute )
63
64
self .audience_id_map = self ._generate_key_map (self .audiences , 'id' , entities .Audience )
64
- self .feature_flag_id_map = self ._generate_key_map (self .feature_flags , 'id' , entities .FeatureFlag )
65
+ self .layer_id_map = self ._generate_key_map (self .layers , 'id' , entities .Layer )
66
+ for layer in self .layer_id_map .values ():
67
+ for experiment in layer .experiments :
68
+ self .experiment_key_map [experiment ['key' ]] = entities .Experiment (** experiment )
69
+
65
70
self .audience_id_map = self ._deserialize_audience (self .audience_id_map )
66
71
for group in self .group_id_map .values ():
67
72
experiments_in_group_key_map = self ._generate_key_map (group .experiments , 'key' , entities .Experiment )
@@ -75,16 +80,29 @@ def __init__(self, datafile, logger, error_handler):
75
80
self .experiment_id_map = {}
76
81
self .variation_key_map = {}
77
82
self .variation_id_map = {}
83
+ self .variation_variable_usage_map = {}
78
84
for experiment in self .experiment_key_map .values ():
79
85
self .experiment_id_map [experiment .id ] = experiment
80
86
self .variation_key_map [experiment .key ] = self ._generate_key_map (
81
87
experiment .variations , 'key' , entities .Variation
82
88
)
83
89
self .variation_id_map [experiment .key ] = {}
84
90
for variation in self .variation_key_map .get (experiment .key ).values ():
85
- feature_flag_to_value_map = self ._map_feature_flag_to_value (variation .variables , self .feature_flag_id_map )
86
- variation .featureFlagMap = feature_flag_to_value_map
87
91
self .variation_id_map [experiment .key ][variation .id ] = variation
92
+ if variation .variables :
93
+ self .variation_variable_usage_map [variation .id ] = self ._generate_key_map (variation .variables , 'id' , entities .Variation .VariableUsage )
94
+
95
+ self .feature_key_map = self ._generate_key_map (self .features , 'key' , entities .Feature )
96
+ for feature in self .feature_key_map .values ():
97
+ feature .variables = self ._generate_key_map (feature .variables , 'key' , entities .Variable )
98
+
99
+ # Check if any of the experiments are in a group and add the group id for faster bucketing later on
100
+ for exp_id in feature .experimentIds :
101
+ experiment_in_feature = self .experiment_id_map [exp_id ]
102
+ if experiment_in_feature .groupId :
103
+ feature .groupId = experiment_in_feature .groupId
104
+ # Experiments in feature can only belong to one mutex group
105
+ break
88
106
89
107
self .parsing_succeeded = True
90
108
@@ -128,45 +146,25 @@ def _deserialize_audience(audience_map):
128
146
return audience_map
129
147
130
148
def _get_typecast_value (self , value , type ):
131
- """ Helper method to determine actual value based on type of feature flag .
149
+ """ Helper method to determine actual value based on type of feature variable .
132
150
133
151
Args:
134
152
value: Value in string form as it was parsed from datafile.
135
153
type: Type denoting the feature flag type.
136
154
137
155
Return:
138
- Value type-casted based on type of feature flag .
156
+ Value type-casted based on type of feature variable .
139
157
"""
140
158
141
- if type == entities .FeatureFlag .Type .BOOLEAN :
159
+ if type == entities .Variable .Type .BOOLEAN :
142
160
return value == 'true'
143
- elif type == entities .FeatureFlag .Type .INTEGER :
161
+ elif type == entities .Variable .Type .INTEGER :
144
162
return int (value )
145
- elif type == entities .FeatureFlag .Type .DOUBLE :
163
+ elif type == entities .Variable .Type .DOUBLE :
146
164
return float (value )
147
165
else :
148
166
return value
149
167
150
- def _map_feature_flag_to_value (self , variables , feature_flag_id_map ):
151
- """ Helper method to create map of feature flag key to associated value for a given variation's feature flag set.
152
-
153
- Args:
154
- variables: List of dicts representing variables on an instance of Variation object.
155
- feature_flag_id_map: Dict mapping feature flag key to feature flag object.
156
-
157
- Returns:
158
- Dict mapping values from feature flag key to value stored on the variation's variable.
159
- """
160
-
161
- feature_flag_value_map = {}
162
- for variable in variables :
163
- feature_flag = feature_flag_id_map [variable .get ('id' )]
164
- if not feature_flag :
165
- continue
166
- feature_flag_value_map [feature_flag .key ] = self ._get_typecast_value (variable .get ('value' ), feature_flag .type )
167
-
168
- return feature_flag_value_map
169
-
170
168
def was_parsing_successful (self ):
171
169
""" Helper method to determine if parsing the datafile was successful.
172
170
@@ -357,15 +355,6 @@ def get_event(self, event_key):
357
355
self .error_handler .handle_error (exceptions .InvalidEventException (enums .Errors .INVALID_EVENT_KEY_ERROR ))
358
356
return None
359
357
360
- def get_revenue_goal (self ):
361
- """ Get the revenue goal for the project.
362
-
363
- Returns:
364
- Revenue goal.
365
- """
366
-
367
- return self .get_event (REVENUE_GOAL_KEY )
368
-
369
358
def get_attribute (self , attribute_key ):
370
359
""" Get attribute for the provided attribute key.
371
360
@@ -384,3 +373,84 @@ def get_attribute(self, attribute_key):
384
373
self .logger .log (enums .LogLevels .ERROR , 'Attribute "%s" is not in datafile.' % attribute_key )
385
374
self .error_handler .handle_error (exceptions .InvalidAttributeException (enums .Errors .INVALID_ATTRIBUTE_ERROR ))
386
375
return None
376
+
377
+ def get_feature_from_key (self , feature_key ):
378
+ """ Get feature for the provided feature key.
379
+
380
+ Args:
381
+ feature_key: Feature key for which feature is to be fetched.
382
+
383
+ Returns:
384
+ Feature corresponding to the provided feature key.
385
+ """
386
+ feature = self .feature_key_map .get (feature_key )
387
+
388
+ if feature :
389
+ return feature
390
+
391
+ self .logger .log (enums .LogLevels .ERROR , 'Feature "%s" is not in datafile.' % feature_key )
392
+ return None
393
+
394
+ def get_layer_from_id (self , layer_id ):
395
+ """ Get layer for the provided layer id.
396
+
397
+ Args:
398
+ layer_id: ID of the layer to be fetched.
399
+
400
+ Returns:
401
+ Layer corresponding to the provided layer id.
402
+ """
403
+ layer = self .layer_id_map .get (layer_id )
404
+
405
+ if layer :
406
+ return layer
407
+
408
+ self .logger .log (enums .LogLevels .ERROR , 'Layer with ID "%s" is not in datafile.' % layer_id )
409
+ return None
410
+
411
+ def get_variable_value_for_variation (self , variable , variation ):
412
+ """ Get the variable value for the given variation.
413
+
414
+ Args:
415
+ Variable: The Variable for which we are getting the value.
416
+ Variation: The Variation for which we are getting the variable value.
417
+
418
+ Returns:
419
+ The type-casted variable value or None if any of the inputs are invalid.
420
+ """
421
+ if not variable or not variation :
422
+ return None
423
+
424
+ if variation .id not in self .variation_variable_usage_map :
425
+ self .logger .log (enums .LogLevels .ERROR , 'Variation with ID "%s" is not in the datafile.' % variation .id )
426
+ return None
427
+
428
+ # Get all variable usages for the given variation
429
+ variable_usages = self .variation_variable_usage_map [variation .id ]
430
+
431
+ # Find usage in given variation
432
+ variable_usage = variable_usages [variable .id ]
433
+
434
+ value = self ._get_typecast_value (variable_usage .value , variable .type )
435
+ return value
436
+
437
+ def get_variable_for_feature (self , feature_key , variable_key ):
438
+ """ Get the variable with the given variable key for the given feature
439
+
440
+ Args:
441
+ feature_key: The key of the feature for which we are getting the variable.
442
+ variable_key: The key of the variable we are getting.
443
+
444
+ Returns:
445
+ Variable with the given key in the given variation.
446
+ """
447
+ feature = self .feature_key_map .get (feature_key )
448
+ if not feature :
449
+ self .logger .log (enums .LogLevels .ERROR , 'Feature with key "%s" not found in the datafile.' % feature_key )
450
+ return None
451
+
452
+ if variable_key not in feature .variables :
453
+ self .logger .log (enums .LogLevels .ERROR , 'Variable with key "%s" not found in the datafile.' % variable_key )
454
+ return None
455
+
456
+ return feature .variables .get (variable_key )
0 commit comments