Skip to content

Commit

Permalink
Merge bad76e7 into 46651fa
Browse files Browse the repository at this point in the history
  • Loading branch information
ozayr-zaviar committed May 3, 2021
2 parents 46651fa + bad76e7 commit f8dd735
Show file tree
Hide file tree
Showing 7 changed files with 559 additions and 131 deletions.
60 changes: 7 additions & 53 deletions optimizely/decision_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -413,39 +413,6 @@ def get_variation_for_rollout(self, project_config, rollout, user_id, attributes

return Decision(None, None, enums.DecisionSources.ROLLOUT), decide_reasons

def get_experiment_in_group(self, project_config, group, bucketing_id):
""" Determine which experiment in the group the user is bucketed into.
Args:
project_config: Instance of ProjectConfig.
group: The group to bucket the user into.
bucketing_id: ID to be used for bucketing the user.
Returns:
Experiment if the user is bucketed into an experiment in the specified group. None otherwise
and array of log messages representing decision making.
"""
decide_reasons = []
experiment_id = self.bucketer.find_bucket(
project_config, bucketing_id, group.id, group.trafficAllocation)
if experiment_id:
experiment = project_config.get_experiment_from_id(experiment_id)
if experiment:
message = 'User with bucketing ID "%s" is in experiment %s of group %s.' % \
(bucketing_id, experiment.key, group.id)
self.logger.info(
message
)
decide_reasons.append(message)
return experiment, decide_reasons
message = 'User with bucketing ID "%s" is not in any experiments of group %s.' % (bucketing_id, group.id)
self.logger.info(
message
)
decide_reasons.append(message)

return None, decide_reasons

def get_variation_for_feature(self, project_config, feature, user_id, attributes=None, ignore_user_profile=False):
""" Returns the experiment/variation the user is bucketed in for the given feature.
Expand All @@ -462,31 +429,18 @@ def get_variation_for_feature(self, project_config, feature, user_id, attributes
decide_reasons = []
bucketing_id, reasons = self._get_bucketing_id(user_id, attributes)
decide_reasons += reasons
# First check if the feature is in a mutex group
if feature.groupId:
group = project_config.get_group(feature.groupId)
if group:
experiment, reasons = self.get_experiment_in_group(project_config, group, bucketing_id)
decide_reasons += reasons
if experiment and experiment.id in feature.experimentIds:

# Check if the feature flag is under an experiment and the the user is bucketed into one of these experiments
if feature.experimentIds:
# Evaluate each experiment ID and return the first bucketed experiment variation
for experiment in feature.experimentIds:
experiment = project_config.get_experiment_from_id(experiment)
if experiment:
variation, variation_reasons = self.get_variation(
project_config, experiment, user_id, attributes, ignore_user_profile)
decide_reasons += variation_reasons
if variation:
return Decision(experiment, variation, enums.DecisionSources.FEATURE_TEST), decide_reasons
else:
self.logger.error(enums.Errors.INVALID_GROUP_ID.format('_get_variation_for_feature'))

# Next check if the feature is being experimented on
elif feature.experimentIds:
# If an experiment is not in a group, then the feature can only be associated with one experiment
experiment = project_config.get_experiment_from_id(feature.experimentIds[0])
if experiment:
variation, variation_reasons = self.get_variation(
project_config, experiment, user_id, attributes, ignore_user_profile)
decide_reasons += variation_reasons
if variation:
return Decision(experiment, variation, enums.DecisionSources.FEATURE_TEST), decide_reasons

# Next check if user is part of a rollout
if feature.rolloutId:
Expand Down
7 changes: 0 additions & 7 deletions optimizely/project_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,13 +128,6 @@ def __init__(self, datafile, logger, error_handler):
# Add this experiment in experiment-feature map.
self.experiment_feature_map[exp_id] = [feature.id]

experiment_in_feature = self.experiment_id_map[exp_id]
# Check if any of the experiments are in a group and add the group id for faster bucketing later on
if experiment_in_feature.groupId:
feature.groupId = experiment_in_feature.groupId
# Experiments in feature can only belong to one mutex group
break

@staticmethod
def _generate_key_map(entity_list, key, entity_class):
""" Helper method to generate map from key to entity object for given list of dicts.
Expand Down
146 changes: 146 additions & 0 deletions tests/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,66 @@ def setUp(self, config_dict='config_dict'):
},
],
},
{
'key': 'test_experiment3',
'status': 'Running',
'layerId': '6',
'audienceIds': [],
'id': '111134',
'forcedVariations': {},
'trafficAllocation': [
{'entityId': '222239', 'endOfRange': 2500},
{'entityId': '', 'endOfRange': 5000},
{'entityId': '', 'endOfRange': 7500},
{'entityId': '', 'endOfRange': 10000}
],
'variations': [
{
'id': '222239',
'key': 'control',
'variables': [],
}
],
},
{
'key': 'test_experiment4',
'status': 'Running',
'layerId': '7',
'audienceIds': [],
'id': '111135',
'forcedVariations': {},
'trafficAllocation': [
{'entityId': '222240', 'endOfRange': 5000},
{'entityId': '', 'endOfRange': 7500},
{'entityId': '', 'endOfRange': 10000}
],
'variations': [
{
'id': '222240',
'key': 'control',
'variables': [],
}
],
},
{
'key': 'test_experiment5',
'status': 'Running',
'layerId': '8',
'audienceIds': [],
'id': '111136',
'forcedVariations': {},
'trafficAllocation': [
{'entityId': '222241', 'endOfRange': 7500},
{'entityId': '', 'endOfRange': 10000}
],
'variations': [
{
'id': '222241',
'key': 'control',
'variables': [],
}
],
},
],
'groups': [
{
Expand Down Expand Up @@ -239,6 +299,72 @@ def setUp(self, config_dict='config_dict'):
{'entityId': '32222', "endOfRange": 3000},
{'entityId': '32223', 'endOfRange': 7500},
],
},
{
'id': '19229',
'policy': 'random',
'experiments': [
{
'id': '42222',
'key': 'group_2_exp_1',
'status': 'Running',
"audienceConditions": [
"or",
"11160"
],
'audienceIds': ['11160'],
'layerId': '211183',
'variations': [
{'key': 'var_1', 'id': '38901'},
],
'forcedVariations': {},
'trafficAllocation': [
{'entityId': '38901', 'endOfRange': 10000}
],
},
{
'id': '42223',
'key': 'group_2_exp_2',
'status': 'Running',
"audienceConditions": [
"or",
"11160"
],
'audienceIds': ['11160'],
'layerId': '211184',
'variations': [
{'key': 'var_1', 'id': '38905'}
],
'forcedVariations': {},
'trafficAllocation': [
{'entityId': '38905', 'endOfRange': 10000}
],
},
{
'id': '42224',
'key': 'group_2_exp_3',
'status': 'Running',
"audienceConditions": [
"or",
"11160"
],
'audienceIds': ['11160'],
'layerId': '211185',
'variations': [
{'key': 'var_1', 'id': '38906'}
],
'forcedVariations': {},
'trafficAllocation': [
{'entityId': '38906', 'endOfRange': 10000}
],
}
],
'trafficAllocation': [
{'entityId': '42222', "endOfRange": 2500},
{'entityId': '42223', 'endOfRange': 5000},
{'entityId': '42224', "endOfRange": 7500},
{'entityId': '', 'endOfRange': 10000},
],
}
],
'attributes': [{'key': 'test_attribute', 'id': '111094'}],
Expand All @@ -255,6 +381,12 @@ def setUp(self, config_dict='config_dict'):
'{"name": "test_attribute", "type": "custom_attribute", "value": "test_value_2"}]]]',
'id': '11159',
},
{
'name': 'Test attribute users 3',
'conditions': "[\"and\", [\"or\", [\"or\", {\"match\": \"exact\", \"name\": \
\"experiment_attr\", \"type\": \"custom_attribute\", \"value\": \"group_experiment\"}]]]",
'id': '11160',
}
],
'rollouts': [
{'id': '201111', 'experiments': []},
Expand Down Expand Up @@ -364,6 +496,20 @@ def setUp(self, config_dict='config_dict'):
'rolloutId': '211111',
'variables': [],
},
{
'id': '91115',
'key': 'test_feature_in_exclusion_group',
'experimentIds': ['42222', '42223', '42224'],
'rolloutId': '211111',
'variables': [],
},
{
'id': '91116',
'key': 'test_feature_in_multiple_experiments',
'experimentIds': ['111134', '111135', '111136'],
'rolloutId': '211111',
'variables': [],
},
],
}

Expand Down
2 changes: 1 addition & 1 deletion tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@ def test_init__with_v4_datafile(self):
'211111',
{'number_of_projects': entities.Variable('131', 'number_of_projects', 'integer', '10')},
),
'test_feature_in_group': entities.FeatureFlag('91113', 'test_feature_in_group', ['32222'], '', {}, '19228'),
'test_feature_in_group': entities.FeatureFlag('91113', 'test_feature_in_group', ['32222'], '', {}),
}

expected_rollout_id_map = {
Expand Down

0 comments on commit f8dd735

Please sign in to comment.