Skip to content

Commit 2477d11

Browse files
lint check should pass
1 parent dd62075 commit 2477d11

File tree

6 files changed

+208
-124
lines changed

6 files changed

+208
-124
lines changed

optimizely/decision_service.py

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,6 @@ def get_variation_for_feature(
479479
Decision namedtuple consisting of experiment and variation for the user.
480480
"""
481481
return self.get_variations_for_feature_list(project_config, [feature], user_context, options)[0]
482-
483482

484483
def validated_forced_decision(
485484
self,
@@ -543,15 +542,15 @@ def validated_forced_decision(
543542
user_context.logger.info(user_has_forced_decision_but_invalid)
544543

545544
return None, reasons
546-
545+
547546
def get_variations_for_feature_list(
548547
self,
549548
project_config: ProjectConfig,
550549
features: list[entities.FeatureFlag],
551550
user_context: OptimizelyUserContext,
552551
options: Optional[Sequence[str]] = None
553-
)->list[tuple[Decision, list[str]]]:
554-
"""
552+
) -> list[tuple[Decision, list[str]]]:
553+
"""
555554
Returns the list of experiment/variation the user is bucketed in for the given list of features.
556555
Args:
557556
project_config: Instance of ProjectConfig.
@@ -563,24 +562,23 @@ def get_variations_for_feature_list(
563562
List of Decision namedtuple consisting of experiment and variation for the user.
564563
"""
565564
decide_reasons: list[str] = []
566-
565+
567566
if options:
568567
ignore_ups = OptimizelyDecideOption.IGNORE_USER_PROFILE_SERVICE in options
569568
else:
570569
ignore_ups = False
571-
572-
570+
573571
user_profile_tracker: Optional[UserProfileTracker] = None
574572
if self.user_profile_service is not None and not ignore_ups:
575573
user_profile_tracker = UserProfileTracker(user_context.user_id, self.user_profile_service, self.logger)
576574
user_profile_tracker.load_user_profile(decide_reasons, None)
577-
575+
578576
decisions = []
579-
577+
580578
for feature in features:
581579
feature_reasons = decide_reasons.copy()
582580
experiment_decision_found = False # Track if an experiment decision was made for the feature
583-
581+
584582
# Check if the feature flag is under an experiment
585583
if feature.experimentIds:
586584
for experiment_id in feature.experimentIds:
@@ -603,28 +601,32 @@ def get_variations_for_feature_list(
603601
feature_reasons.extend(variation_reasons)
604602

605603
if decision_variation:
606-
self.logger.debug(f'User "{user_context.user_id}" bucketed into experiment "{experiment.key}" of feature "{feature.key}".')
604+
self.logger.debug(
605+
'User "{}" bucketed into experiment "{}" of feature "{}".'.format(
606+
user_context.user_id, experiment.key, feature.key)
607+
)
607608
decision = Decision(experiment, decision_variation, enums.DecisionSources.FEATURE_TEST)
608609
decisions.append((decision, feature_reasons))
609610
experiment_decision_found = True # Mark that a decision was found
610611
break # Stop after the first successful experiment decision
611-
612+
612613
# Only process rollout if no experiment decision was found
613614
if not experiment_decision_found:
614-
rollout_decision, rollout_reasons = self.get_variation_for_rollout(project_config, feature, user_context)
615+
rollout_decision, rollout_reasons = self.get_variation_for_rollout(project_config,
616+
feature,
617+
user_context)
615618
if rollout_reasons:
616619
feature_reasons.extend(rollout_reasons)
617620
if rollout_decision:
618-
self.logger.debug(f'User "{user_context.user_id}" bucketed into rollout for feature "{feature.key}".')
621+
self.logger.debug(f'User "{user_context.user_id}" '
622+
f'bucketed into rollout for feature "{feature.key}".')
619623
else:
620-
self.logger.debug(f'User "{user_context.user_id}" not bucketed into any rollout for feature "{feature.key}".')
624+
self.logger.debug(f'User "{user_context.user_id}" '
625+
f'not bucketed into any rollout for feature "{feature.key}".')
621626

622627
decisions.append((rollout_decision, feature_reasons))
623-
628+
624629
if self.user_profile_service is not None and user_profile_tracker is not None and ignore_ups is False:
625630
user_profile_tracker.save_user_profile()
626-
627-
return decisions
628631

629-
630-
632+
return decisions

optimizely/optimizely.py

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,10 @@ def get_variation(
633633

634634
user_context = OptimizelyUserContext(self, self.logger, user_id, attributes, False)
635635
user_profile_tracker = user_profile.UserProfileTracker(user_id, self.user_profile_service, self.logger)
636-
variation, _ = self.decision_service.get_variation(project_config, experiment, user_context, user_profile_tracker)
636+
variation, _ = self.decision_service.get_variation(project_config,
637+
experiment,
638+
user_context,
639+
user_profile_tracker)
637640
if variation:
638641
variation_key = variation.key
639642

@@ -1123,16 +1126,16 @@ def _decide(
11231126

11241127
if OptimizelyDecideOption.ENABLED_FLAGS_ONLY in decide_options:
11251128
decide_options.remove(OptimizelyDecideOption.ENABLED_FLAGS_ONLY)
1126-
1129+
11271130
decision = self._decide_for_keys(
11281131
user_context,
11291132
[key],
11301133
decide_options,
11311134
True
11321135
)[key]
1133-
1136+
11341137
return decision
1135-
1138+
11361139
def _create_optimizely_decision(
11371140
self,
11381141
user_context: OptimizelyUserContext,
@@ -1147,23 +1150,26 @@ def _create_optimizely_decision(
11471150
if flag_decision.variation is not None:
11481151
if flag_decision.variation.featureEnabled:
11491152
feature_enabled = True
1150-
1153+
11511154
self.logger.info(f'Feature {flag_key} is enabled for user {user_id} {feature_enabled}"')
1152-
1155+
11531156
# Create Optimizely Decision Result.
11541157
attributes = user_context.get_user_attributes()
11551158
rule_key = flag_decision.experiment.key if flag_decision.experiment else None
11561159
all_variables = {}
11571160
decision_source = flag_decision.source
11581161
decision_event_dispatched = False
1159-
1162+
11601163
feature_flag = project_config.feature_key_map.get(flag_key)
11611164

11621165
# Send impression event if Decision came from a feature
11631166
# test and decide options doesn't include disableDecisionEvent
11641167
if OptimizelyDecideOption.DISABLE_DECISION_EVENT not in decide_options:
11651168
if decision_source == DecisionSources.FEATURE_TEST or project_config.send_flag_decisions:
1166-
self._send_impression_event(project_config, flag_decision.experiment, flag_decision.variation, flag_key, rule_key or '',
1169+
self._send_impression_event(project_config,
1170+
flag_decision.experiment,
1171+
flag_decision.variation,
1172+
flag_key, rule_key or '',
11671173
str(decision_source), feature_enabled,
11681174
user_id, attributes)
11691175

@@ -1189,7 +1195,11 @@ def _create_optimizely_decision(
11891195
all_variables[variable_key] = actual_value
11901196

11911197
should_include_reasons = OptimizelyDecideOption.INCLUDE_REASONS in decide_options
1192-
variation_key = flag_decision.variation.key if flag_decision is not None and flag_decision.variation is not None else None
1198+
variation_key = (
1199+
flag_decision.variation.key
1200+
if flag_decision is not None and flag_decision.variation is not None
1201+
else None
1202+
)
11931203
# Send notification
11941204
self.notification_center.send_notifications(
11951205
enums.NotificationTypes.DECISION,
@@ -1212,7 +1222,6 @@ def _create_optimizely_decision(
12121222
rule_key=rule_key, flag_key=flag_key,
12131223
user_context=user_context, reasons=decision_reasons if should_include_reasons else []
12141224
)
1215-
12161225

12171226
def _decide_all(
12181227
self,
@@ -1282,7 +1291,7 @@ def _decide_for_keys(
12821291
self.logger.debug('Provided decide options is not an array. Using default decide options.')
12831292
merged_decide_options = self.default_decide_options
12841293

1285-
enabled_flags_only = OptimizelyDecideOption.ENABLED_FLAGS_ONLY in merged_decide_options
1294+
# enabled_flags_only = OptimizelyDecideOption.ENABLED_FLAGS_ONLY in merged_decide_options
12861295

12871296
decisions: dict[str, OptimizelyDecision] = {}
12881297
valid_keys = []
@@ -1292,11 +1301,11 @@ def _decide_for_keys(
12921301
# if enabled_flags_only and not decision.enabled:
12931302
# continue
12941303
# decisions[key] = decision
1295-
1304+
12961305
project_config = self.config_manager.get_config()
12971306
flags_without_forced_decision: list[entities.FeatureFlag] = []
12981307
flag_decisions: dict[str, Decision] = {}
1299-
1308+
13001309
if project_config is None:
13011310
return decisions
13021311
for key in keys:
@@ -1307,39 +1316,34 @@ def _decide_for_keys(
13071316
valid_keys.append(key)
13081317
decision_reasons: list[str] = []
13091318
decision_reasons_dict[key] = decision_reasons
1310-
1319+
13111320
optimizely_decision_context = OptimizelyUserContext.OptimizelyDecisionContext(flag_key=key, rule_key=None)
13121321
forced_decision_response = self.decision_service.validated_forced_decision(project_config,
13131322
optimizely_decision_context,
13141323
user_context)
13151324
variation, decision_reasons = forced_decision_response
13161325
decision_reasons_dict[key] += decision_reasons
1317-
1326+
13181327
if variation:
13191328
decision = Decision(None, variation, enums.DecisionSources.FEATURE_TEST)
13201329
flag_decisions[key] = decision
13211330
else:
1322-
# Regular decision
1323-
# decision, decision_reasons = self.decision_service.get_variation_for_feature(project_config,
1324-
# feature_flag,
1325-
# user_context, decide_options)
13261331
flags_without_forced_decision.append(feature_flag)
13271332

1328-
13291333
decision_list = self.decision_service.get_variations_for_feature_list(
13301334
project_config,
13311335
flags_without_forced_decision,
13321336
user_context,
13331337
merged_decide_options
13341338
)
1335-
1339+
13361340
for i in range(0, len(flags_without_forced_decision)):
13371341
decision = decision_list[i][0]
13381342
reasons = decision_list[i][1]
13391343
flag_key = flags_without_forced_decision[i].key
13401344
flag_decisions[flag_key] = decision
13411345
decision_reasons_dict[flag_key] += reasons
1342-
1346+
13431347
print(decision_reasons_dict)
13441348
for key in valid_keys:
13451349
flag_decision = flag_decisions[key]
@@ -1352,10 +1356,11 @@ def _decide_for_keys(
13521356
merged_decide_options,
13531357
project_config
13541358
)
1355-
1356-
if (OptimizelyDecideOption.ENABLED_FLAGS_ONLY not in merged_decide_options) or (optimizely_decision.enabled):
1359+
enabled_flags_only_missing = OptimizelyDecideOption.ENABLED_FLAGS_ONLY not in merged_decide_options
1360+
is_enabled = optimizely_decision.enabled
1361+
if enabled_flags_only_missing or is_enabled:
13571362
decisions[key] = optimizely_decision
1358-
1363+
13591364
return decisions
13601365

13611366
def _setup_odp(self, sdk_key: Optional[str]) -> None:

optimizely/user_profile.py

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,13 @@
1616
from sys import version_info
1717
from . import logger as _logging
1818
from . import decision_service
19-
from .helpers import enums
2019
if version_info < (3, 8):
2120
from typing_extensions import Final
2221
else:
2322
from typing import Final, TYPE_CHECKING # type: ignore
24-
23+
2524
if TYPE_CHECKING:
2625
# prevent circular dependenacy by skipping import at runtime
27-
from .project_config import ProjectConfig
28-
from .logger import Logger
2926
from .entities import Experiment, Variation
3027
from .decision_service import Decision
3128
from optimizely.error_handler import BaseErrorHandler
@@ -106,18 +103,23 @@ def save(self, user_profile: dict[str, Any]) -> None:
106103
"""
107104
pass
108105

106+
109107
class UserProfileTracker:
110-
def __init__(self, user_id: str, user_profile_service: Optional[UserProfileService], logger:Optional[_logging.Logger] = None):
108+
def __init__(self,
109+
user_id: str,
110+
user_profile_service: Optional[UserProfileService],
111+
logger: Optional[_logging.Logger] = None):
111112
self.user_id = user_id
112113
self.user_profile_service = user_profile_service
113114
self.logger = _logging.adapt_logger(logger or _logging.NoOpLogger())
114115
self.profile_updated = False
115116
self.user_profile = UserProfile(user_id, {})
116-
117+
117118
def get_user_profile(self) -> UserProfile:
118119
return self.user_profile
119120

120-
def load_user_profile(self, reasons: Optional[list[str]]=[], error_handler: Optional[BaseErrorHandler]=None) -> None:
121+
def load_user_profile(self, reasons: Optional[list[str]] = [],
122+
error_handler: Optional[BaseErrorHandler] = None) -> None:
121123
reasons = reasons if reasons else []
122124
try:
123125
user_profile = self.user_profile_service.lookup(self.user_id) if self.user_profile_service else None
@@ -128,7 +130,7 @@ def load_user_profile(self, reasons: Optional[list[str]]=[], error_handler: Opti
128130
else:
129131
if 'user_id' in user_profile and 'experiment_bucket_map' in user_profile:
130132
self.user_profile = UserProfile(
131-
user_profile['user_id'],
133+
user_profile['user_id'],
132134
user_profile['experiment_bucket_map']
133135
)
134136
self.logger.info("User profile loaded successfully.")
@@ -142,10 +144,10 @@ def load_user_profile(self, reasons: Optional[list[str]]=[], error_handler: Opti
142144
self.logger.exception(f'Unable to retrieve user profile for user "{self.user_id}"as lookup failed.')
143145
# Todo: add error handler
144146
# error_handler.handle_error()
145-
147+
146148
if self.user_profile is None:
147149
self.user_profile = UserProfile(self.user_id, {})
148-
150+
149151
def update_user_profile(self, experiment: Experiment, variation: Variation) -> None:
150152
if experiment.id in self.user_profile.experiment_bucket_map:
151153
decision = self.user_profile.experiment_bucket_map[experiment.id]
@@ -157,12 +159,10 @@ def update_user_profile(self, experiment: Experiment, variation: Variation) -> N
157159
)
158160
else:
159161
decision = decision_service.Decision(experiment=None, variation=variation, source=None)
160-
162+
161163
self.user_profile.experiment_bucket_map[experiment.id] = decision
162164
self.profile_updated = True
163-
# self.logger.info(f'Updated variation "{variation.id}" of experiment "{experiment.id}" for user "{self.user_profile.user_id}".')
164-
165-
165+
166166
def save_user_profile(self, error_handler: Optional[BaseErrorHandler] = None) -> None:
167167
if not self.profile_updated:
168168
return
@@ -171,5 +171,6 @@ def save_user_profile(self, error_handler: Optional[BaseErrorHandler] = None) ->
171171
self.user_profile_service.save(self.user_profile.__dict__)
172172
self.logger.info(f'Saved user profile of user "{self.user_profile.user_id}".')
173173
except Exception as exception:
174-
self.logger.warning(f'Failed to save user profile of user "{self.user_profile.user_id}".')
174+
self.logger.warning(f'Failed to save user profile of user "{self.user_profile.user_id}" '
175+
f'for exception:{exception}".')
175176
# error_handler.handle_error(exception)

0 commit comments

Comments
 (0)