From b46b95ee0f50678259ebaf4df82b31b25e30f489 Mon Sep 17 00:00:00 2001 From: Owais Akbani Date: Thu, 5 Dec 2019 17:01:47 +0500 Subject: [PATCH 01/13] Structure Optimizely Config --- optimizely/optimizely.py | 14 ++++++++ optimizely/optimizely_config.py | 63 +++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 optimizely/optimizely_config.py diff --git a/optimizely/optimizely.py b/optimizely/optimizely.py index ba82adb8..ee7754f4 100644 --- a/optimizely/optimizely.py +++ b/optimizely/optimizely.py @@ -25,6 +25,7 @@ from .event_dispatcher import EventDispatcher as default_event_dispatcher from .helpers import enums, validator from .notification_center import NotificationCenter +from .optimizely_config import OptimizelyConfigBuilder class Optimizely(object): @@ -733,3 +734,16 @@ def get_forced_variation(self, experiment_key, user_id): forced_variation = self.decision_service.get_forced_variation(project_config, experiment_key, user_id) return forced_variation.key if forced_variation else None + + + def get_optimizely_config(): + if not self.is_valid: + self.logger.error(enums.Errors.INVALID_OPTIMIZELY.format('get_optimizely_config')) + return None + + project_config = self.config_manager.get_config() + if not project_config: + self.logger.error(enums.Errors.INVALID_PROJECT_CONFIG.format('get_optimizely_config')) + return None + + return OptimizelyConfigBuilder.get_config() diff --git a/optimizely/optimizely_config.py b/optimizely/optimizely_config.py new file mode 100644 index 00000000..5318d7ec --- /dev/null +++ b/optimizely/optimizely_config.py @@ -0,0 +1,63 @@ +# Copyright 2019, Optimizely +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +class OptimizelyConfig(object): + def __init__(self, project_config): + self.revision = None + self.experiments_map = None + self.features_map = None + +class OptimizelyConfigExperiment(object): + def __init__(self, id, key, variations_map): + self.id = id + self.key = key + self.variations_map = variations_map + +class OptimizelyConfigFeature(object): + def __init__(self, id, key, experiments_map): + self.id = id + self.key = key + self.experiments_map = experiments_map + +class OptimizelyConfigVariation(object): + def __init__(self, id, key, variables_map): + self.id = id + self.key = key + self.variables_map = + +class OptimizelyConfigVariable(object): + def __init__(self, id, key, type, value): + self.id = id + self.key = key + self.type = type + self.value = value + +class OptimizelyConfigBuilder(object): + + @staticmethod + def get_config(): + pass + + @staticmethod + def _get_features_map(): + pass + + @staticmethod + def _get_merged_variables_map(): + pass + + @staticmethod + def _get_experiments_map() + pass + + From 0f2cf5a64da9d8d6707710747a38f3132c5078c8 Mon Sep 17 00:00:00 2001 From: Owais Akbani Date: Fri, 6 Dec 2019 12:59:10 +0500 Subject: [PATCH 02/13] Some more work --- optimizely/optimizely.py | 2 +- optimizely/optimizely_config.py | 179 +++++++++++++++++++++++++++----- 2 files changed, 155 insertions(+), 26 deletions(-) diff --git a/optimizely/optimizely.py b/optimizely/optimizely.py index ee7754f4..de7a8295 100644 --- a/optimizely/optimizely.py +++ b/optimizely/optimizely.py @@ -746,4 +746,4 @@ def get_optimizely_config(): self.logger.error(enums.Errors.INVALID_PROJECT_CONFIG.format('get_optimizely_config')) return None - return OptimizelyConfigBuilder.get_config() + return OptimizelyConfigBuilder.get_config(project_config) diff --git a/optimizely/optimizely_config.py b/optimizely/optimizely_config.py index 5318d7ec..281aee19 100644 --- a/optimizely/optimizely_config.py +++ b/optimizely/optimizely_config.py @@ -11,53 +11,182 @@ # See the License for the specific language governing permissions and # limitations under the License. +import json + class OptimizelyConfig(object): - def __init__(self, project_config): - self.revision = None - self.experiments_map = None - self.features_map = None + def __init__(self, revision, experiments_map, features_map): + self.revision = revision + self.experimentsMap = experiments_map + self.featuresMap = features_map -class OptimizelyConfigExperiment(object): +class OptimizelyExperiment(object): def __init__(self, id, key, variations_map): self.id = id self.key = key - self.variations_map = variations_map + self.variationsMap = variations_map -class OptimizelyConfigFeature(object): - def __init__(self, id, key, experiments_map): +class OptimizelyFeature(object): + def __init__(self, id, key, experiments_map, variables_map): self.id = id self.key = key - self.experiments_map = experiments_map + self.experimentsMap = experiments_map + self.variablesMap = variables_map -class OptimizelyConfigVariation(object): - def __init__(self, id, key, variables_map): +class OptimizelyVariation(object): + def __init__(self, id, key, feature_enabled, variables_map): self.id = id self.key = key - self.variables_map = + self.featureEnabled = feature_enabled + self.variablesMap = variables_map -class OptimizelyConfigVariable(object): +class OptimizelyVariable(object): def __init__(self, id, key, type, value): self.id = id self.key = key self.type = type self.value = value + class OptimizelyConfigBuilder(object): - @staticmethod - def get_config(): - pass + def __init__(self, project_config): + self.experiments = project_config.get('experiments', []) + self.feature_flags = project_config.get('featureFlags', []) + self.groups = config.get('groups', []) + self.revision = config.get('revision') + + def get_optimizely_config(self): + experiments_map = self._get_experiments_map() + features_map = self._get_features_map(experiments_map) + + return OptimizelyConfig(self.revision, experiments_map, features_map) + + def _get_feature_variable_by_id(self, variable_id, feature_flag): + for variable in feature_flag.get('variables', None): + if variable_id == variable['id']: + return variable + return None + + def _get_featureflag_by_experiment_id(self, experiment_id, feature_flags): + for feature in feature_flags: + for id in feature['experimentIds']: + if id == experiment_id: + return feature + return None + + def _get_experiment_by_id(self, experiment_id, experiments_map): + for experiment in experiments_map.values(): + if experiment.id == experiment_id: + return experiment + + return None + + def _get_variables_map(self, variation, experiment, feature_flags): + feature_flag = self._get_featureflag_by_experiment_id(experiment['id'], feature_flags) + if feature_flag is None: + return {} + + # set default variables for each variation + variables_map = {} + for variable in feature_flag.get('variables', []): + opt_variable = OptimizelyVariable( + variable['id'], variable['key'], variable['type'], variable['defaultValue'] + ) + variables_map[variable['key']] = opt_variable + + + # set variation specific variable value if any + if variation.get('featureEnabled', None): + for variable in variation.get('variables', []): + feature_variable = self._get_feature_variable_by_id(variable['id'], feature_flag) + variables_map[feature_variable['key']].value = variable['value'] + + return variables_map + + def _get_variations_map(self, experiment): + variations_map = {} + + for variation in experiment.get('variations', []): + variables_map = self._get_variables_map(variation, experiment, self.feature_flags) + feature_enabled = variation.get('featureEnabled', None) + + optly_variation = OptimizelyVariation( + variation['id'], variation['key'], feature_enabled, variables_map + ) + + variations_map[variation['key']] = optly_variation + + return variations_map + + def _get_all_experiments(self): + experiments = self.experiments + + for group in self.groups: + experiments = experiments + group.experiments + + return experiments + + def _get_experiments_map(self): + experiments_map = {} + all_experiments = self._get_all_experiments() + + for exp in all_experiments: + optly_exp = OptimizelyExperiment( + exp['id'], exp['key'], self._get_variations_map(exp) + ) + + experiments_map[exp['key']] = optly_exp + + return experiments_map + + def _get_features_map(self, experiments_map): + features_map = {} + + for feature in self.feature_flags: + exp_map = {} + for experiment_id in feature.get('experimentIds', []): + optly_exp = self._get_experiment_by_id(experiment_id, experiments_map) + exp_map[optly_exp.key] = optly_exp + + + + variables_map = {} + for variable in feature['variables']: + optly_variable = OptimizelyVariable( + variable['id'], variable['key'], variable['type'], variable['defaultValue'] + ) + + variables_map[variable['key']] = optly_variable + + + optly_feature = OptimizelyFeature( + feature['id'], feature['key'], exp_map, variables_map + ) + + features_map[feature['key']] = optly_feature + + + return features_map + + + +def _readfile(): + with open('feature_variables.json', 'r') as datafile: + return datafile.read() + + +datafile = _readfile() +config = json.loads(datafile) + +opt_builder = OptimizelyConfigBuilder(config) + +response = opt_builder.get_optimizely_config() - @staticmethod - def _get_features_map(): - pass - @staticmethod - def _get_merged_variables_map(): - pass +import pprint +pp = pprint.PrettyPrinter(indent=2) +pp.pprint(json.dumps(response.__dict__, default= lambda o: o.__dict__)) - @staticmethod - def _get_experiments_map() - pass +# print(response.__dict__) From e2cccb8ee226e26f342c73dcb71100f7f24b70e4 Mon Sep 17 00:00:00 2001 From: Owais Akbani Date: Fri, 6 Dec 2019 16:47:20 +0500 Subject: [PATCH 03/13] Works on this --- optimizely/optimizely.py | 4 +-- optimizely/optimizely_config.py | 43 ++++++++------------------------- 2 files changed, 12 insertions(+), 35 deletions(-) diff --git a/optimizely/optimizely.py b/optimizely/optimizely.py index de7a8295..ee75d481 100644 --- a/optimizely/optimizely.py +++ b/optimizely/optimizely.py @@ -736,7 +736,7 @@ def get_forced_variation(self, experiment_key, user_id): return forced_variation.key if forced_variation else None - def get_optimizely_config(): + def get_optimizely_config(self): if not self.is_valid: self.logger.error(enums.Errors.INVALID_OPTIMIZELY.format('get_optimizely_config')) return None @@ -746,4 +746,4 @@ def get_optimizely_config(): self.logger.error(enums.Errors.INVALID_PROJECT_CONFIG.format('get_optimizely_config')) return None - return OptimizelyConfigBuilder.get_config(project_config) + return OptimizelyConfigBuilder(project_config).get_optimizely_config() diff --git a/optimizely/optimizely_config.py b/optimizely/optimizely_config.py index 281aee19..35679f8b 100644 --- a/optimizely/optimizely_config.py +++ b/optimizely/optimizely_config.py @@ -50,10 +50,10 @@ def __init__(self, id, key, type, value): class OptimizelyConfigBuilder(object): def __init__(self, project_config): - self.experiments = project_config.get('experiments', []) - self.feature_flags = project_config.get('featureFlags', []) - self.groups = config.get('groups', []) - self.revision = config.get('revision') + self.experiments = project_config.experiments + self.feature_flags = project_config.feature_flags + self.groups = project_config.groups + self.revision = project_config.revision def get_optimizely_config(self): experiments_map = self._get_experiments_map() @@ -62,13 +62,13 @@ def get_optimizely_config(self): return OptimizelyConfig(self.revision, experiments_map, features_map) def _get_feature_variable_by_id(self, variable_id, feature_flag): - for variable in feature_flag.get('variables', None): + for variable in feature_flag.get('variables', []): if variable_id == variable['id']: return variable return None - def _get_featureflag_by_experiment_id(self, experiment_id, feature_flags): - for feature in feature_flags: + def _get_featureflag_by_experiment_id(self, experiment_id): + for feature in self.feature_flags: for id in feature['experimentIds']: if id == experiment_id: return feature @@ -81,8 +81,8 @@ def _get_experiment_by_id(self, experiment_id, experiments_map): return None - def _get_variables_map(self, variation, experiment, feature_flags): - feature_flag = self._get_featureflag_by_experiment_id(experiment['id'], feature_flags) + def _get_variables_map(self, variation, experiment): + feature_flag = self._get_featureflag_by_experiment_id(experiment['id']) if feature_flag is None: return {} @@ -107,7 +107,7 @@ def _get_variations_map(self, experiment): variations_map = {} for variation in experiment.get('variations', []): - variables_map = self._get_variables_map(variation, experiment, self.feature_flags) + variables_map = self._get_variables_map(variation, experiment) feature_enabled = variation.get('featureEnabled', None) optly_variation = OptimizelyVariation( @@ -167,26 +167,3 @@ def _get_features_map(self, experiments_map): return features_map - - - -def _readfile(): - with open('feature_variables.json', 'r') as datafile: - return datafile.read() - - -datafile = _readfile() -config = json.loads(datafile) - -opt_builder = OptimizelyConfigBuilder(config) - -response = opt_builder.get_optimizely_config() - - -import pprint -pp = pprint.PrettyPrinter(indent=2) -pp.pprint(json.dumps(response.__dict__, default= lambda o: o.__dict__)) - - -# print(response.__dict__) - From 4deaf60812c4979e0ea799f215c12547fd7444d2 Mon Sep 17 00:00:00 2001 From: Owais Akbani Date: Fri, 6 Dec 2019 20:00:15 +0500 Subject: [PATCH 04/13] Optimized by lookup maps --- optimizely/optimizely.py | 3 +- optimizely/optimizely_config.py | 103 +++++++++++++++----------------- 2 files changed, 50 insertions(+), 56 deletions(-) diff --git a/optimizely/optimizely.py b/optimizely/optimizely.py index ee75d481..d4dd68df 100644 --- a/optimizely/optimizely.py +++ b/optimizely/optimizely.py @@ -735,7 +735,6 @@ def get_forced_variation(self, experiment_key, user_id): forced_variation = self.decision_service.get_forced_variation(project_config, experiment_key, user_id) return forced_variation.key if forced_variation else None - def get_optimizely_config(self): if not self.is_valid: self.logger.error(enums.Errors.INVALID_OPTIMIZELY.format('get_optimizely_config')) @@ -746,4 +745,4 @@ def get_optimizely_config(self): self.logger.error(enums.Errors.INVALID_PROJECT_CONFIG.format('get_optimizely_config')) return None - return OptimizelyConfigBuilder(project_config).get_optimizely_config() + return OptimizelyConfigBuilder(project_config).build() diff --git a/optimizely/optimizely_config.py b/optimizely/optimizely_config.py index 35679f8b..48af744e 100644 --- a/optimizely/optimizely_config.py +++ b/optimizely/optimizely_config.py @@ -11,33 +11,38 @@ # See the License for the specific language governing permissions and # limitations under the License. -import json +import copy + class OptimizelyConfig(object): def __init__(self, revision, experiments_map, features_map): self.revision = revision - self.experimentsMap = experiments_map - self.featuresMap = features_map + self.experiments_map = experiments_map + self.features_map = features_map + class OptimizelyExperiment(object): def __init__(self, id, key, variations_map): self.id = id self.key = key - self.variationsMap = variations_map + self.variations_map = variations_map + class OptimizelyFeature(object): def __init__(self, id, key, experiments_map, variables_map): self.id = id self.key = key - self.experimentsMap = experiments_map - self.variablesMap = variables_map + self.experiments_map = experiments_map + self.variables_map = variables_map + class OptimizelyVariation(object): def __init__(self, id, key, feature_enabled, variables_map): self.id = id self.key = key - self.featureEnabled = feature_enabled - self.variablesMap = variables_map + self.feature_enabled = feature_enabled + self.variables_map = variables_map + class OptimizelyVariable(object): def __init__(self, id, key, type, value): @@ -55,53 +60,51 @@ def __init__(self, project_config): self.groups = project_config.groups self.revision = project_config.revision - def get_optimizely_config(self): - experiments_map = self._get_experiments_map() - features_map = self._get_features_map(experiments_map) + def build(self): + self._create_lookup_maps() - return OptimizelyConfig(self.revision, experiments_map, features_map) + experiments_key_map, experiments_id_map = self._get_experiments_maps() + features_map = self._get_features_map(experiments_id_map) - def _get_feature_variable_by_id(self, variable_id, feature_flag): - for variable in feature_flag.get('variables', []): - if variable_id == variable['id']: - return variable - return None + return OptimizelyConfig(self.revision, experiments_key_map, features_map) - def _get_featureflag_by_experiment_id(self, experiment_id): + def _create_lookup_maps(self): + self.exp_id_to_feature_map = {} for feature in self.feature_flags: for id in feature['experimentIds']: - if id == experiment_id: - return feature - return None + self.exp_id_to_feature_map[id] = feature - def _get_experiment_by_id(self, experiment_id, experiments_map): - for experiment in experiments_map.values(): - if experiment.id == experiment_id: - return experiment + self.feature_key_variable_key_to_variable_map = {} + self.feature_key_variable_id_to_variable_map = {} + for feature in self.feature_flags: + variables_key_map = {} + variables_id_map = {} + for variable in feature.get('variables', []): + opt_variable = OptimizelyVariable( + variable['id'], variable['key'], variable['type'], variable['defaultValue'] + ) + variables_key_map[variable['key']] = opt_variable + variables_id_map[variable['id']] = opt_variable - return None + self.feature_key_variable_key_to_variable_map[feature['key']] = variables_key_map + self.feature_key_variable_id_to_variable_map[feature['key']] = variables_id_map def _get_variables_map(self, variation, experiment): - feature_flag = self._get_featureflag_by_experiment_id(experiment['id']) + feature_flag = self.exp_id_to_feature_map.get(experiment['id'], None) if feature_flag is None: return {} # set default variables for each variation variables_map = {} - for variable in feature_flag.get('variables', []): - opt_variable = OptimizelyVariable( - variable['id'], variable['key'], variable['type'], variable['defaultValue'] - ) - variables_map[variable['key']] = opt_variable - + variables_map = copy.deepcopy(self.feature_key_variable_key_to_variable_map[feature_flag['key']]) # set variation specific variable value if any if variation.get('featureEnabled', None): for variable in variation.get('variables', []): - feature_variable = self._get_feature_variable_by_id(variable['id'], feature_flag) - variables_map[feature_variable['key']].value = variable['value'] + feature_variable = self.feature_key_variable_id_to_variable_map[feature_flag['key']][variable['id']] + variables_map[feature_variable.key].value = variable['value'] - return variables_map + return variables_map def _get_variations_map(self, experiment): variations_map = {} @@ -126,38 +129,31 @@ def _get_all_experiments(self): return experiments - def _get_experiments_map(self): - experiments_map = {} + def _get_experiments_maps(self): + experiments_key_map = {} + experiments_id_map = {} all_experiments = self._get_all_experiments() - + for exp in all_experiments: optly_exp = OptimizelyExperiment( exp['id'], exp['key'], self._get_variations_map(exp) ) - experiments_map[exp['key']] = optly_exp + experiments_key_map[exp['key']] = optly_exp + experiments_id_map[exp['id']] = optly_exp - return experiments_map + return experiments_key_map, experiments_id_map - def _get_features_map(self, experiments_map): + def _get_features_map(self, experiments_id_map): features_map = {} for feature in self.feature_flags: exp_map = {} for experiment_id in feature.get('experimentIds', []): - optly_exp = self._get_experiment_by_id(experiment_id, experiments_map) + optly_exp = experiments_id_map[experiment_id] exp_map[optly_exp.key] = optly_exp - - - variables_map = {} - for variable in feature['variables']: - optly_variable = OptimizelyVariable( - variable['id'], variable['key'], variable['type'], variable['defaultValue'] - ) - - variables_map[variable['key']] = optly_variable - + variables_map = self.feature_key_variable_key_to_variable_map[feature['key']] optly_feature = OptimizelyFeature( feature['id'], feature['key'], exp_map, variables_map @@ -165,5 +161,4 @@ def _get_features_map(self, experiments_map): features_map[feature['key']] = optly_feature - return features_map From 6f2a79fad5fabae24b5d513465f044f3fc0eeea9 Mon Sep 17 00:00:00 2001 From: Owais Akbani Date: Mon, 9 Dec 2019 19:21:28 +0500 Subject: [PATCH 05/13] Add comments and unit tests --- optimizely/optimizely.py | 10 ++++-- optimizely/optimizely_config.py | 59 ++++++++++++++++++++++++++++++--- tests/test_optimizely.py | 34 +++++++++++++++++++ 3 files changed, 96 insertions(+), 7 deletions(-) diff --git a/optimizely/optimizely.py b/optimizely/optimizely.py index d4dd68df..fdaea2e6 100644 --- a/optimizely/optimizely.py +++ b/optimizely/optimizely.py @@ -25,7 +25,7 @@ from .event_dispatcher import EventDispatcher as default_event_dispatcher from .helpers import enums, validator from .notification_center import NotificationCenter -from .optimizely_config import OptimizelyConfigBuilder +from .optimizely_config import OptimizelyConfigService class Optimizely(object): @@ -736,6 +736,12 @@ def get_forced_variation(self, experiment_key, user_id): return forced_variation.key if forced_variation else None def get_optimizely_config(self): + """ Gets OptimizelyConfig instance for the current project config. + + Returns: + OptimizelyConfig instance. None if the optimizely instance is invalid or + project config isn't available. + """ if not self.is_valid: self.logger.error(enums.Errors.INVALID_OPTIMIZELY.format('get_optimizely_config')) return None @@ -745,4 +751,4 @@ def get_optimizely_config(self): self.logger.error(enums.Errors.INVALID_PROJECT_CONFIG.format('get_optimizely_config')) return None - return OptimizelyConfigBuilder(project_config).build() + return OptimizelyConfigService(project_config).get_optimizely_config() diff --git a/optimizely/optimizely_config.py b/optimizely/optimizely_config.py index 48af744e..161a163e 100644 --- a/optimizely/optimizely_config.py +++ b/optimizely/optimizely_config.py @@ -52,15 +52,25 @@ def __init__(self, id, key, type, value): self.value = value -class OptimizelyConfigBuilder(object): +class OptimizelyConfigService(object): + """ Class encapsulating methods to be used in creating instance of OptimizelyConfig. """ def __init__(self, project_config): + """ + Arguments: + project_config ProjectConfig + """ self.experiments = project_config.experiments self.feature_flags = project_config.feature_flags self.groups = project_config.groups self.revision = project_config.revision - def build(self): + def get_optimizely_config(self): + """ Returns instance of OptimizelyConfig + + Returns: + Optimizely Config instance. + """ self._create_lookup_maps() experiments_key_map, experiments_id_map = self._get_experiments_maps() @@ -69,6 +79,8 @@ def build(self): return OptimizelyConfig(self.revision, experiments_key_map, features_map) def _create_lookup_maps(self): + """ Creates lookup maps to avoid redundant iteration of config objects. """ + self.exp_id_to_feature_map = {} for feature in self.feature_flags: for id in feature['experimentIds']: @@ -90,6 +102,15 @@ def _create_lookup_maps(self): self.feature_key_variable_id_to_variable_map[feature['key']] = variables_id_map def _get_variables_map(self, variation, experiment): + """ Gets variables map for given variation and experiment. + + Arguments: + variation dict + experiment dict + + Returns: + dict - Map of variable key to OptimizelyVariable for the given variation. + """ feature_flag = self.exp_id_to_feature_map.get(experiment['id'], None) if feature_flag is None: return {} @@ -99,7 +120,7 @@ def _get_variables_map(self, variation, experiment): variables_map = copy.deepcopy(self.feature_key_variable_key_to_variable_map[feature_flag['key']]) # set variation specific variable value if any - if variation.get('featureEnabled', None): + if variation.get('featureEnabled'): for variable in variation.get('variables', []): feature_variable = self.feature_key_variable_id_to_variable_map[feature_flag['key']][variable['id']] variables_map[feature_variable.key].value = variable['value'] @@ -107,6 +128,14 @@ def _get_variables_map(self, variation, experiment): return variables_map def _get_variations_map(self, experiment): + """ Gets variation map for the given experiment. + + Arguments: + experiment dict + + Returns: + dict -- Map of variation key to OptimizelyVariation. + """ variations_map = {} for variation in experiment.get('variations', []): @@ -122,18 +151,30 @@ def _get_variations_map(self, experiment): return variations_map def _get_all_experiments(self): + """ Gets all experiments in the project config. + + Returns: + list -- List of dicts of experiments. + """ experiments = self.experiments for group in self.groups: - experiments = experiments + group.experiments + experiments = experiments + group['experiments'] return experiments def _get_experiments_maps(self): + """ Gets maps for all the experiments in the project config. + + Returns: + dict, dict -- experiment key/id to OptimizelyExperiment maps. + """ + # Key map is required for the OptimizelyConfig response. experiments_key_map = {} + # Id map comes in handy to figure out feature experiment. experiments_id_map = {} - all_experiments = self._get_all_experiments() + all_experiments = self._get_all_experiments() for exp in all_experiments: optly_exp = OptimizelyExperiment( exp['id'], exp['key'], self._get_variations_map(exp) @@ -145,6 +186,14 @@ def _get_experiments_maps(self): return experiments_key_map, experiments_id_map def _get_features_map(self, experiments_id_map): + """ Gets features map for the project config. + + Arguments: + experiments_id_map dict -- experiment id to OptimizelyExperiment map + + Returns: + dict -- feaure key to OptimizelyFeature map + """ features_map = {} for feature in self.feature_flags: diff --git a/tests/test_optimizely.py b/tests/test_optimizely.py index 39978451..409a3138 100644 --- a/tests/test_optimizely.py +++ b/tests/test_optimizely.py @@ -23,6 +23,7 @@ from optimizely import exceptions from optimizely import logger from optimizely import optimizely +from optimizely import optimizely_config from optimizely import project_config from optimizely import version from optimizely.event.event_factory import EventFactory @@ -3911,6 +3912,39 @@ def test_get_feature_variable_returns__default_value__complex_audience_match(sel self.assertEqual(10, opt_obj.get_feature_variable_integer('feat2_with_var', 'z', 'user1', {})) self.assertEqual(10, opt_obj.get_feature_variable('feat2_with_var', 'z', 'user1', {})) + def test_get_optimizely_config__invalid_object(self): + """ Test that get_optimizely_config logs error if Optimizely instance is invalid. """ + + class InvalidConfigManager(object): + pass + + opt_obj = optimizely.Optimizely(json.dumps(self.config_dict), config_manager=InvalidConfigManager()) + + with mock.patch.object(opt_obj, 'logger') as mock_client_logging: + self.assertIsNone(opt_obj.get_optimizely_config()) + + mock_client_logging.error.assert_called_once_with( + 'Optimizely instance is not valid. Failing "get_optimizely_config".') + + def test_get_optimizely_config__invalid_config(self): + """ Test that get_optimizely_config logs error if config is invalid. """ + + opt_obj = optimizely.Optimizely('invalid_datafile') + + with mock.patch.object(opt_obj, 'logger') as mock_client_logging: + self.assertIsNone(opt_obj.get_optimizely_config()) + + mock_client_logging.error.assert_called_once_with( + 'Invalid config. Optimizely instance is not valid. ' 'Failing "get_optimizely_config".' + ) + + def test_get_optimizely_config_returns_instance_of_optimizely_config(self): + """ Test that get_optimizely_config returns an instance of OptimizelyConfig. """ + + opt_obj = optimizely.Optimizely(json.dumps(self.config_dict_with_features)) + opt_config = opt_obj.get_optimizely_config() + self.assertIsInstance(opt_config, optimizely_config.OptimizelyConfig) + class OptimizelyWithExceptionTest(base.BaseTest): def setUp(self): From 7709dff8c7f5fc12d73f7ab4c91bc86c9e0857ae Mon Sep 17 00:00:00 2001 From: Owais Akbani Date: Tue, 10 Dec 2019 19:49:19 +0500 Subject: [PATCH 06/13] address comments --- optimizely/optimizely.py | 2 +- optimizely/optimizely_config.py | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/optimizely/optimizely.py b/optimizely/optimizely.py index fdaea2e6..8c30aac8 100644 --- a/optimizely/optimizely.py +++ b/optimizely/optimizely.py @@ -751,4 +751,4 @@ def get_optimizely_config(self): self.logger.error(enums.Errors.INVALID_PROJECT_CONFIG.format('get_optimizely_config')) return None - return OptimizelyConfigService(project_config).get_optimizely_config() + return OptimizelyConfigService(project_config).get_config() diff --git a/optimizely/optimizely_config.py b/optimizely/optimizely_config.py index 161a163e..f922ebb3 100644 --- a/optimizely/optimizely_config.py +++ b/optimizely/optimizely_config.py @@ -45,10 +45,10 @@ def __init__(self, id, key, feature_enabled, variables_map): class OptimizelyVariable(object): - def __init__(self, id, key, type, value): + def __init__(self, id, key, variable_type, value): self.id = id self.key = key - self.type = type + self.type = variable_type self.value = value @@ -57,7 +57,7 @@ class OptimizelyConfigService(object): def __init__(self, project_config): """ - Arguments: + Args: project_config ProjectConfig """ self.experiments = project_config.experiments @@ -65,13 +65,14 @@ def __init__(self, project_config): self.groups = project_config.groups self.revision = project_config.revision - def get_optimizely_config(self): + self._create_lookup_maps() + + def get_config(self): """ Returns instance of OptimizelyConfig Returns: Optimizely Config instance. """ - self._create_lookup_maps() experiments_key_map, experiments_id_map = self._get_experiments_maps() features_map = self._get_features_map(experiments_id_map) @@ -104,7 +105,7 @@ def _create_lookup_maps(self): def _get_variables_map(self, variation, experiment): """ Gets variables map for given variation and experiment. - Arguments: + Args: variation dict experiment dict @@ -130,7 +131,7 @@ def _get_variables_map(self, variation, experiment): def _get_variations_map(self, experiment): """ Gets variation map for the given experiment. - Arguments: + Args: experiment dict Returns: @@ -188,7 +189,7 @@ def _get_experiments_maps(self): def _get_features_map(self, experiments_id_map): """ Gets features map for the project config. - Arguments: + Args: experiments_id_map dict -- experiment id to OptimizelyExperiment map Returns: From 7a9cb0833568bd02b56603d90b885b24689fda9b Mon Sep 17 00:00:00 2001 From: Owais Akbani Date: Tue, 10 Dec 2019 22:31:50 +0500 Subject: [PATCH 07/13] tests: unit tests --- tests/base.py | 6 +- tests/test_optimizely_config.py | 467 ++++++++++++++++++++++++++++++++ 2 files changed, 469 insertions(+), 4 deletions(-) create mode 100644 tests/test_optimizely_config.py diff --git a/tests/base.py b/tests/base.py index 2b2e2802..815baa8a 100644 --- a/tests/base.py +++ b/tests/base.py @@ -182,14 +182,12 @@ def setUp(self, config_dict='config_dict'): { 'id': '122239', 'key': 'control', - 'featureEnabled': True, - 'variables': [{'id': '155551', 'value': '42.42'}], + 'variables': [], }, { 'id': '122240', 'key': 'variation', - 'featureEnabled': True, - 'variables': [{'id': '155551', 'value': '13.37'}], + 'variables': [], }, ], }, diff --git a/tests/test_optimizely_config.py b/tests/test_optimizely_config.py new file mode 100644 index 00000000..501a4ed6 --- /dev/null +++ b/tests/test_optimizely_config.py @@ -0,0 +1,467 @@ +# Copyright 2019, Optimizely +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json + +from optimizely import optimizely +from optimizely import optimizely_config +from . import base + + +class OptimizelyConfigTest(base.BaseTest): + def setUp(self): + base.BaseTest.setUp(self) + opt_instance = optimizely.Optimizely(json.dumps(self.config_dict_with_features)) + self.project_config = opt_instance.config_manager.get_config() + self.opt_config_service = optimizely_config.OptimizelyConfigService(self.project_config) + + self.expected_config = { + 'experiments_map': { + 'test_experiment2': { + 'variations_map': { + 'control': { + 'variables_map': { + + }, + 'id': '122239', + 'key': 'control', + 'feature_enabled': None + }, + 'variation': { + 'variables_map': { + + }, + 'id': '122240', + 'key': 'variation', + 'feature_enabled': None + } + }, + 'id': '111133', + 'key': 'test_experiment2' + }, + 'test_experiment': { + 'variations_map': { + 'control': { + 'variables_map': { + 'environment': { + 'key': 'environment', + 'type': 'string', + 'id': '128', + 'value': 'devel' + }, + 'count': { + 'key': 'count', + 'type': 'integer', + 'id': '130', + 'value': '999' + }, + 'is_working': { + 'key': 'is_working', + 'type': 'boolean', + 'id': '127', + 'value': 'true' + }, + 'cost': { + 'key': 'cost', + 'type': 'double', + 'id': '129', + 'value': '10.99' + }, + 'variable_without_usage': { + 'key': 'variable_without_usage', + 'type': 'integer', + 'id': '131', + 'value': '45' + } + }, + 'id': '111128', + 'key': 'control', + 'feature_enabled': False + }, + 'variation': { + 'variables_map': { + 'environment': { + 'key': 'environment', + 'type': 'string', + 'id': '128', + 'value': 'staging' + }, + 'count': { + 'key': 'count', + 'type': 'integer', + 'id': '130', + 'value': '4243' + }, + 'is_working': { + 'key': 'is_working', + 'type': 'boolean', + 'id': '127', + 'value': 'true' + }, + 'cost': { + 'key': 'cost', + 'type': 'double', + 'id': '129', + 'value': '10.02' + }, + 'variable_without_usage': { + 'key': 'variable_without_usage', + 'type': 'integer', + 'id': '131', + 'value': '45' + } + }, + 'id': '111129', + 'key': 'variation', + 'feature_enabled': True + } + }, + 'id': '111127', + 'key': 'test_experiment' + }, + 'group_exp_1': { + 'variations_map': { + 'group_exp_1_variation': { + 'variables_map': { + + }, + 'id': '28902', + 'key': 'group_exp_1_variation', + 'feature_enabled': None + }, + 'group_exp_1_control': { + 'variables_map': { + + }, + 'id': '28901', + 'key': 'group_exp_1_control', + 'feature_enabled': None + } + }, + 'id': '32222', + 'key': 'group_exp_1' + }, + 'group_exp_2': { + 'variations_map': { + 'group_exp_2_variation': { + 'variables_map': { + + }, + 'id': '28906', + 'key': 'group_exp_2_variation', + 'feature_enabled': None + }, + 'group_exp_2_control': { + 'variables_map': { + + }, + 'id': '28905', + 'key': 'group_exp_2_control', + 'feature_enabled': None + } + }, + 'id': '32223', + 'key': 'group_exp_2' + } + }, + 'features_map': { + 'test_feature_in_experiment': { + 'variables_map': { + 'environment': { + 'key': 'environment', + 'type': 'string', + 'id': '128', + 'value': 'devel' + }, + 'count': { + 'key': 'count', + 'type': 'integer', + 'id': '130', + 'value': '999' + }, + 'is_working': { + 'key': 'is_working', + 'type': 'boolean', + 'id': '127', + 'value': 'true' + }, + 'cost': { + 'key': 'cost', + 'type': 'double', + 'id': '129', + 'value': '10.99' + }, + 'variable_without_usage': { + 'key': 'variable_without_usage', + 'type': 'integer', + 'id': '131', + 'value': '45' + } + }, + 'experiments_map': { + 'test_experiment': { + 'variations_map': { + 'control': { + 'variables_map': { + 'environment': { + 'key': 'environment', + 'type': 'string', + 'id': '128', + 'value': 'devel' + }, + 'count': { + 'key': 'count', + 'type': 'integer', + 'id': '130', + 'value': '999' + }, + 'is_working': { + 'key': 'is_working', + 'type': 'boolean', + 'id': '127', + 'value': 'true' + }, + 'cost': { + 'key': 'cost', + 'type': 'double', + 'id': '129', + 'value': '10.99' + }, + 'variable_without_usage': { + 'key': 'variable_without_usage', + 'type': 'integer', + 'id': '131', + 'value': '45' + } + }, + 'id': '111128', + 'key': 'control', + 'feature_enabled': False + }, + 'variation': { + 'variables_map': { + 'environment': { + 'key': 'environment', + 'type': 'string', + 'id': '128', + 'value': 'staging' + }, + 'count': { + 'key': 'count', + 'type': 'integer', + 'id': '130', + 'value': '4243' + }, + 'is_working': { + 'key': 'is_working', + 'type': 'boolean', + 'id': '127', + 'value': 'true' + }, + 'cost': { + 'key': 'cost', + 'type': 'double', + 'id': '129', + 'value': '10.02' + }, + 'variable_without_usage': { + 'key': 'variable_without_usage', + 'type': 'integer', + 'id': '131', + 'value': '45' + } + }, + 'id': '111129', + 'key': 'variation', + 'feature_enabled': True + } + }, + 'id': '111127', + 'key': 'test_experiment' + } + }, + 'id': '91111', + 'key': 'test_feature_in_experiment' + }, + 'test_feature_in_rollout': { + 'variables_map': { + 'count': { + 'key': 'count', + 'type': 'integer', + 'id': '135', + 'value': '999' + }, + 'message': { + 'key': 'message', + 'type': 'string', + 'id': '133', + 'value': 'Hello' + }, + 'price': { + 'key': 'price', + 'type': 'double', + 'id': '134', + 'value': '99.99' + }, + 'is_running': { + 'key': 'is_running', + 'type': 'boolean', + 'id': '132', + 'value': 'false' + } + }, + 'experiments_map': { + + }, + 'id': '91112', + 'key': 'test_feature_in_rollout' + }, + 'test_feature_in_group': { + 'variables_map': { + + }, + 'experiments_map': { + 'group_exp_1': { + 'variations_map': { + 'group_exp_1_variation': { + 'variables_map': { + + }, + 'id': '28902', + 'key': 'group_exp_1_variation', + 'feature_enabled': None + }, + 'group_exp_1_control': { + 'variables_map': { + + }, + 'id': '28901', + 'key': 'group_exp_1_control', + 'feature_enabled': None + } + }, + 'id': '32222', + 'key': 'group_exp_1' + } + }, + 'id': '91113', + 'key': 'test_feature_in_group' + }, + 'test_feature_in_experiment_and_rollout': { + 'variables_map': { + + }, + 'experiments_map': { + 'group_exp_2': { + 'variations_map': { + 'group_exp_2_variation': { + 'variables_map': { + + }, + 'id': '28906', + 'key': 'group_exp_2_variation', + 'feature_enabled': None + }, + 'group_exp_2_control': { + 'variables_map': { + + }, + 'id': '28905', + 'key': 'group_exp_2_control', + 'feature_enabled': None + } + }, + 'id': '32223', + 'key': 'group_exp_2' + } + }, + 'id': '91114', + 'key': 'test_feature_in_experiment_and_rollout' + } + }, + 'revision': '1' + } + + self.actual_config = self.opt_config_service.get_config() + self.actual_config_dict = self.to_dict(self.actual_config) + + def to_dict(self, obj): + return json.loads(json.dumps(obj, default=lambda o: o.__dict__)) + + def test__get_config(self): + """ Test that get_config returns an expected instance of OptimizelyConfig. """ + + self.assertIsInstance(self.actual_config, optimizely_config.OptimizelyConfig) + self.assertEqual(self.expected_config, self.actual_config_dict) + + def test__get_experiments_maps(self): + """ Test that get_experiments_map returns expected experiment key and id maps. """ + + actual_key_map, actual_id_map = self.opt_config_service._get_experiments_maps() + expected_key_map = self.expected_config['experiments_map'] + + self.assertIsInstance(actual_key_map, dict) + for exp in actual_key_map.values(): + self.assertIsInstance(exp, optimizely_config.OptimizelyExperiment) + + self.assertEqual(expected_key_map, self.to_dict(actual_key_map)) + + expected_id_map = {} + for exp in expected_key_map.values(): + expected_id_map[exp['id']] = exp + + self.assertEqual(expected_id_map, self.to_dict(actual_id_map)) + + def test__get_features_map(self): + """ Test that get_features_map returns expected features map. """ + + exp_key_map, exp_id_map = self.opt_config_service._get_experiments_maps() + + actual_feature_map = self.opt_config_service._get_features_map(exp_id_map) + expected_feature_map = self.expected_config['features_map'] + + self.assertIsInstance(actual_feature_map, dict) + for feat in actual_feature_map.values(): + self.assertIsInstance(feat, optimizely_config.OptimizelyFeature) + + self.assertEqual(expected_feature_map, self.to_dict(actual_feature_map)) + + def test__get_variations_map(self): + """ Test that get_variations_map returns expected variations map. """ + + experiment = self.project_config.experiments[0] + actual_variations_map = self.opt_config_service._get_variations_map(experiment) + + expected_variations_map = self.expected_config['experiments_map']['test_experiment']['variations_map'] + + self.assertIsInstance(actual_variations_map, dict) + for variation in actual_variations_map.values(): + self.assertIsInstance(variation, optimizely_config.OptimizelyVariation) + + self.assertEqual(expected_variations_map, self.to_dict(actual_variations_map)) + + def test__get_variables_map(self): + """ Test that get_variables_map returns expected variables map. """ + + experiment = self.project_config.experiments[0] + variation = experiment['variations'][0] + actual_variables_map = self.opt_config_service._get_variables_map(variation, experiment) + + expected_variations_map = self.expected_config['experiments_map']['test_experiment']['variations_map'] + expected_variables_map = expected_variations_map['control']['variables_map'] + + self.assertIsInstance(actual_variables_map, dict) + for variable in actual_variables_map.values(): + self.assertIsInstance(variable, optimizely_config.OptimizelyVariable) + + self.assertEqual(expected_variables_map, self.to_dict(actual_variables_map)) From 15bcb7669074a86b58ec017c3cae63e4ae0f5ba8 Mon Sep 17 00:00:00 2001 From: Owais Akbani Date: Thu, 12 Dec 2019 16:05:05 +0500 Subject: [PATCH 08/13] address comments --- optimizely/optimizely_config.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/optimizely/optimizely_config.py b/optimizely/optimizely_config.py index f922ebb3..0595478d 100644 --- a/optimizely/optimizely_config.py +++ b/optimizely/optimizely_config.py @@ -83,13 +83,13 @@ def _create_lookup_maps(self): """ Creates lookup maps to avoid redundant iteration of config objects. """ self.exp_id_to_feature_map = {} - for feature in self.feature_flags: - for id in feature['experimentIds']: - self.exp_id_to_feature_map[id] = feature - self.feature_key_variable_key_to_variable_map = {} - self.feature_key_variable_id_to_variable_map = {} + self.feature_key_variable_id_to_variable_map = {} + for feature in self.feature_flags: + for experiment_id in feature['experimentIds']: + self.exp_id_to_feature_map[experiment_id] = feature + variables_key_map = {} variables_id_map = {} for variable in feature.get('variables', []): From 720d3502d4fba66e95adcd5f03508495d0bd9fbb Mon Sep 17 00:00:00 2001 From: Owais Akbani Date: Thu, 12 Dec 2019 16:07:48 +0500 Subject: [PATCH 09/13] fix: lint --- optimizely/optimizely_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/optimizely/optimizely_config.py b/optimizely/optimizely_config.py index 0595478d..2d3e92cf 100644 --- a/optimizely/optimizely_config.py +++ b/optimizely/optimizely_config.py @@ -84,7 +84,7 @@ def _create_lookup_maps(self): self.exp_id_to_feature_map = {} self.feature_key_variable_key_to_variable_map = {} - self.feature_key_variable_id_to_variable_map = {} + self.feature_key_variable_id_to_variable_map = {} for feature in self.feature_flags: for experiment_id in feature['experimentIds']: From caad32d3084be46d720adde471729a74e189831e Mon Sep 17 00:00:00 2001 From: Owais Akbani Date: Mon, 16 Dec 2019 14:14:33 +0500 Subject: [PATCH 10/13] validate project config --- optimizely/optimizely_config.py | 13 ++++++++++++- tests/test_optimizely_config.py | 6 ++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/optimizely/optimizely_config.py b/optimizely/optimizely_config.py index 2d3e92cf..f39a21b8 100644 --- a/optimizely/optimizely_config.py +++ b/optimizely/optimizely_config.py @@ -13,6 +13,8 @@ import copy +from .project_config import ProjectConfig + class OptimizelyConfig(object): def __init__(self, revision, experiments_map, features_map): @@ -60,6 +62,12 @@ def __init__(self, project_config): Args: project_config ProjectConfig """ + self.is_valid = True + + if not isinstance(project_config, ProjectConfig): + self.is_valid = False + return + self.experiments = project_config.experiments self.feature_flags = project_config.feature_flags self.groups = project_config.groups @@ -71,9 +79,12 @@ def get_config(self): """ Returns instance of OptimizelyConfig Returns: - Optimizely Config instance. + Optimizely Config instance or None if OptimizelyConfigService is invalid. """ + if not self.is_valid: + return None + experiments_key_map, experiments_id_map = self._get_experiments_maps() features_map = self._get_features_map(experiments_id_map) diff --git a/tests/test_optimizely_config.py b/tests/test_optimizely_config.py index 501a4ed6..12db6b49 100644 --- a/tests/test_optimizely_config.py +++ b/tests/test_optimizely_config.py @@ -404,6 +404,12 @@ def test__get_config(self): self.assertIsInstance(self.actual_config, optimizely_config.OptimizelyConfig) self.assertEqual(self.expected_config, self.actual_config_dict) + def test__get_config__invalid_project_config(self): + """ Test that get_config returns None when invalid project config supplied. """ + + opt_service = optimizely_config.OptimizelyConfigService({"key": "invalid"}) + self.assertIsNone(opt_service.get_config()) + def test__get_experiments_maps(self): """ Test that get_experiments_map returns expected experiment key and id maps. """ From dcc7389cac8ac1e7fadded6c854225f9dfb4aeda Mon Sep 17 00:00:00 2001 From: Owais Akbani Date: Tue, 31 Dec 2019 13:11:07 +0500 Subject: [PATCH 11/13] nit --- optimizely/optimizely_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/optimizely/optimizely_config.py b/optimizely/optimizely_config.py index f39a21b8..f1dd9998 100644 --- a/optimizely/optimizely_config.py +++ b/optimizely/optimizely_config.py @@ -76,7 +76,7 @@ def __init__(self, project_config): self._create_lookup_maps() def get_config(self): - """ Returns instance of OptimizelyConfig + """ Gets instance of OptimizelyConfig Returns: Optimizely Config instance or None if OptimizelyConfigService is invalid. From 19bb7fd271ba5f264e1e90e65c316838f36ad6ea Mon Sep 17 00:00:00 2001 From: Owais Akbani Date: Tue, 31 Dec 2019 13:15:25 +0500 Subject: [PATCH 12/13] headers --- optimizely/optimizely.py | 2 +- optimizely/optimizely_config.py | 2 +- tests/base.py | 2 +- tests/test_optimizely.py | 2 +- tests/test_optimizely_config.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/optimizely/optimizely.py b/optimizely/optimizely.py index 8c30aac8..72496edc 100644 --- a/optimizely/optimizely.py +++ b/optimizely/optimizely.py @@ -1,4 +1,4 @@ -# Copyright 2016-2019, Optimizely +# Copyright 2016-2020, Optimizely # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at diff --git a/optimizely/optimizely_config.py b/optimizely/optimizely_config.py index f1dd9998..898af18b 100644 --- a/optimizely/optimizely_config.py +++ b/optimizely/optimizely_config.py @@ -1,4 +1,4 @@ -# Copyright 2019, Optimizely +# Copyright 2020, Optimizely # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at diff --git a/tests/base.py b/tests/base.py index 815baa8a..48d28857 100644 --- a/tests/base.py +++ b/tests/base.py @@ -1,4 +1,4 @@ -# Copyright 2016-2019, Optimizely +# Copyright 2016-2020, Optimizely # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at diff --git a/tests/test_optimizely.py b/tests/test_optimizely.py index 409a3138..44bbf27e 100644 --- a/tests/test_optimizely.py +++ b/tests/test_optimizely.py @@ -1,4 +1,4 @@ -# Copyright 2016-2019, Optimizely +# Copyright 2016-2020, Optimizely # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at diff --git a/tests/test_optimizely_config.py b/tests/test_optimizely_config.py index 12db6b49..6cbae94e 100644 --- a/tests/test_optimizely_config.py +++ b/tests/test_optimizely_config.py @@ -1,4 +1,4 @@ -# Copyright 2019, Optimizely +# Copyright 2020, Optimizely # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at From a95c6693dfc0c61e73273542d55f4cca06068f6d Mon Sep 17 00:00:00 2001 From: Owais Akbani Date: Fri, 3 Jan 2020 16:34:57 +0500 Subject: [PATCH 13/13] refact: nits --- optimizely/optimizely_config.py | 12 ++++++------ tests/test_optimizely_config.py | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/optimizely/optimizely_config.py b/optimizely/optimizely_config.py index 898af18b..9fcc0948 100644 --- a/optimizely/optimizely_config.py +++ b/optimizely/optimizely_config.py @@ -113,12 +113,12 @@ def _create_lookup_maps(self): self.feature_key_variable_key_to_variable_map[feature['key']] = variables_key_map self.feature_key_variable_id_to_variable_map[feature['key']] = variables_id_map - def _get_variables_map(self, variation, experiment): - """ Gets variables map for given variation and experiment. + def _get_variables_map(self, experiment, variation): + """ Gets variables map for given experiment and variation. Args: - variation dict - experiment dict + experiment dict -- Experiment parsed from the datafile. + variation dict -- Variation of the given experiment. Returns: dict - Map of variable key to OptimizelyVariable for the given variation. @@ -143,7 +143,7 @@ def _get_variations_map(self, experiment): """ Gets variation map for the given experiment. Args: - experiment dict + experiment dict -- Experiment parsed from the datafile. Returns: dict -- Map of variation key to OptimizelyVariation. @@ -151,7 +151,7 @@ def _get_variations_map(self, experiment): variations_map = {} for variation in experiment.get('variations', []): - variables_map = self._get_variables_map(variation, experiment) + variables_map = self._get_variables_map(experiment, variation) feature_enabled = variation.get('featureEnabled', None) optly_variation = OptimizelyVariation( diff --git a/tests/test_optimizely_config.py b/tests/test_optimizely_config.py index 6cbae94e..495325ea 100644 --- a/tests/test_optimizely_config.py +++ b/tests/test_optimizely_config.py @@ -461,7 +461,7 @@ def test__get_variables_map(self): experiment = self.project_config.experiments[0] variation = experiment['variations'][0] - actual_variables_map = self.opt_config_service._get_variables_map(variation, experiment) + actual_variables_map = self.opt_config_service._get_variables_map(experiment, variation) expected_variations_map = self.expected_config['experiments_map']['test_experiment']['variations_map'] expected_variables_map = expected_variations_map['control']['variables_map']