Skip to content

Commit ef9114b

Browse files
committed
Revert unnecessary changes
1 parent c505bc0 commit ef9114b

File tree

5 files changed

+108
-127
lines changed

5 files changed

+108
-127
lines changed

optimizely/decision_service.py

Lines changed: 102 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -730,102 +730,21 @@ def get_decision_for_flag(
730730
'reasons': reasons
731731
}
732732

733-
# Check if the feature flag has an experiment and the user is bucketed into that experiment
734-
if feature_flag.experimentIds:
735-
# Iterate through experiments to find a match
736-
for experiment_id in feature_flag.experimentIds:
737-
experiment = project_config.get_experiment_from_id(experiment_id)
738-
if not experiment:
739-
continue
740-
741-
# Check for forced decision
742-
optimizely_decision_context = OptimizelyUserContext.OptimizelyDecisionContext(
743-
feature_flag.key, experiment.key
744-
)
745-
forced_decision_variation, forced_reasons = self.validated_forced_decision(
746-
project_config, optimizely_decision_context, user_context
747-
)
748-
reasons.extend(forced_reasons)
749-
750-
if forced_decision_variation:
751-
decision = Decision(
752-
experiment, forced_decision_variation, enums.DecisionSources.FEATURE_TEST, None
753-
)
754-
return {
755-
'decision': decision,
756-
'error': False,
757-
'reasons': reasons
758-
}
759-
760-
# Get variation through normal bucketing
761-
variation_result = self.get_variation(
762-
project_config, experiment, user_context, user_profile_tracker, reasons, decide_options
763-
)
764-
cmab_uuid = variation_result['cmab_uuid']
765-
variation_reasons = variation_result['reasons']
766-
decision_variation = variation_result['variation']
767-
error = variation_result['error']
768-
reasons.extend(variation_reasons)
769-
770-
# If there's an error, return immediately
771-
if error:
772-
decision = Decision(experiment, None, enums.DecisionSources.FEATURE_TEST, cmab_uuid)
773-
return {
774-
'decision': decision,
775-
'error': True,
776-
'reasons': reasons
777-
}
778-
779-
# If user is bucketed into a variation, return the decision
780-
if decision_variation:
781-
self.logger.debug(
782-
f'User "{user_id}" '
783-
f'bucketed into experiment "{experiment.key}" of feature "{feature_flag.key}".'
784-
)
785-
decision = Decision(
786-
experiment, decision_variation, enums.DecisionSources.FEATURE_TEST, cmab_uuid
787-
)
788-
return {
789-
'decision': decision,
790-
'error': False,
791-
'reasons': reasons
792-
}
793-
794-
# Check if the feature flag has a rollout and the user is bucketed into that rollout
795-
rollout_decision, rollout_reasons = self.get_variation_for_rollout(
796-
project_config, feature_flag, user_context
797-
)
798-
reasons.extend(rollout_reasons)
799-
800-
if rollout_decision and rollout_decision.variation:
801-
# Check if this was a forced decision (last reason contains "forced decision map")
802-
is_forced_decision = reasons and 'forced decision map' in reasons[-1] if reasons else False
803-
804-
if not is_forced_decision:
805-
# Only add the "bucketed into rollout" message for normal bucketing
806-
message = (
807-
f"The user '{user_id}' is bucketed into a rollout "
808-
f"for feature flag '{feature_flag.key}'."
809-
)
810-
self.logger.info(message)
811-
reasons.append(message)
733+
# If no holdout decision, fall back to existing experiment/rollout logic
734+
# Use get_variations_for_feature_list which handles experiments and rollouts
735+
fallback_result = self.get_variations_for_feature_list(
736+
project_config, [feature_flag], user_context, decide_options
737+
)[0]
812738

813-
return {
814-
'decision': rollout_decision,
815-
'error': False,
816-
'reasons': reasons
817-
}
818-
else:
819-
message = (
820-
f"The user '{user_id}' is not bucketed into a rollout "
821-
f"for feature flag '{feature_flag.key}'."
822-
)
823-
self.logger.info(message)
824-
return {
825-
'decision': Decision(None, None, enums.DecisionSources.ROLLOUT, None),
826-
'error': False,
827-
'reasons': reasons
828-
}
739+
# Merge reasons
740+
if fallback_result.get('reasons'):
741+
reasons.extend(fallback_result['reasons'])
742+
743+
return {
744+
'decision': fallback_result['decision'],
745+
'error': fallback_result.get('error', False),
746+
'reasons': reasons
747+
}
829748

830749
def get_variation_for_holdout(
831750
self,
@@ -905,9 +824,9 @@ def get_variation_for_holdout(
905824
self.logger.info(message)
906825
decide_reasons.append(message)
907826

908-
# Create Decision for holdout - pass holdout dict as experiment, source is HOLDOUT
827+
# Create Decision for holdout - experiment is None, source is HOLDOUT
909828
holdout_decision: Decision = Decision(
910-
experiment=holdout, # type: ignore[arg-type]
829+
experiment=None,
911830
variation=variation,
912831
source=enums.DecisionSources.HOLDOUT,
913832
cmab_uuid=None
@@ -1012,24 +931,100 @@ def get_variations_for_feature_list(
1012931
- 'error': Boolean indicating if an error occurred during the decision process.
1013932
- 'reasons': List of log messages representing decision making for each feature.
1014933
"""
1015-
ignore_ups = False
934+
decide_reasons: list[str] = []
935+
1016936
if options:
1017937
ignore_ups = OptimizelyDecideOption.IGNORE_USER_PROFILE_SERVICE in options
938+
else:
939+
ignore_ups = False
1018940

1019941
user_profile_tracker: Optional[UserProfileTracker] = None
1020942
if self.user_profile_service is not None and not ignore_ups:
1021-
user_id = user_context.user_id
1022-
user_profile_tracker = UserProfileTracker(user_id, self.user_profile_service, self.logger)
1023-
user_profile_tracker.load_user_profile([], None)
943+
user_profile_tracker = UserProfileTracker(user_context.user_id, self.user_profile_service, self.logger)
944+
user_profile_tracker.load_user_profile(decide_reasons, None)
1024945

1025946
decisions = []
1026-
for feature_flag in features:
1027-
decision = self.get_decision_for_flag(
1028-
feature_flag, user_context, project_config, options, user_profile_tracker
1029-
)
1030-
decisions.append(decision)
1031947

1032-
if user_profile_tracker:
948+
for feature in features:
949+
feature_reasons = decide_reasons.copy()
950+
experiment_decision_found = False # Track if an experiment decision was made for the feature
951+
952+
# Check if the feature flag is under an experiment
953+
if feature.experimentIds:
954+
for experiment_id in feature.experimentIds:
955+
experiment = project_config.get_experiment_from_id(experiment_id)
956+
decision_variation: Optional[Union[entities.Variation, VariationDict]] = None
957+
958+
if experiment:
959+
optimizely_decision_context = OptimizelyUserContext.OptimizelyDecisionContext(
960+
feature.key, experiment.key)
961+
forced_decision_variation, reasons_received = self.validated_forced_decision(
962+
project_config, optimizely_decision_context, user_context)
963+
feature_reasons.extend(reasons_received)
964+
965+
if forced_decision_variation:
966+
decision_variation = forced_decision_variation
967+
cmab_uuid = None
968+
error = False
969+
else:
970+
variation_result = self.get_variation(
971+
project_config, experiment, user_context, user_profile_tracker, feature_reasons, options
972+
)
973+
cmab_uuid = variation_result['cmab_uuid']
974+
variation_reasons = variation_result['reasons']
975+
decision_variation = variation_result['variation']
976+
error = variation_result['error']
977+
feature_reasons.extend(variation_reasons)
978+
979+
if error:
980+
decision = Decision(experiment, None, enums.DecisionSources.FEATURE_TEST, cmab_uuid)
981+
decision_result: DecisionResult = {
982+
'decision': decision,
983+
'error': True,
984+
'reasons': feature_reasons
985+
}
986+
decisions.append(decision_result)
987+
experiment_decision_found = True
988+
break
989+
990+
if decision_variation:
991+
self.logger.debug(
992+
f'User "{user_context.user_id}" '
993+
f'bucketed into experiment "{experiment.key}" of feature "{feature.key}".'
994+
)
995+
decision = Decision(experiment, decision_variation,
996+
enums.DecisionSources.FEATURE_TEST, cmab_uuid)
997+
decision_result = {
998+
'decision': decision,
999+
'error': False,
1000+
'reasons': feature_reasons
1001+
}
1002+
decisions.append(decision_result)
1003+
experiment_decision_found = True # Mark that a decision was found
1004+
break # Stop after the first successful experiment decision
1005+
1006+
# Only process rollout if no experiment decision was found and no error
1007+
if not experiment_decision_found:
1008+
rollout_decision, rollout_reasons = self.get_variation_for_rollout(project_config,
1009+
feature,
1010+
user_context)
1011+
if rollout_reasons:
1012+
feature_reasons.extend(rollout_reasons)
1013+
if rollout_decision:
1014+
self.logger.debug(f'User "{user_context.user_id}" '
1015+
f'bucketed into rollout for feature "{feature.key}".')
1016+
else:
1017+
self.logger.debug(f'User "{user_context.user_id}" '
1018+
f'not bucketed into any rollout for feature "{feature.key}".')
1019+
1020+
decision_result = {
1021+
'decision': rollout_decision,
1022+
'error': False,
1023+
'reasons': feature_reasons
1024+
}
1025+
decisions.append(decision_result)
1026+
1027+
if self.user_profile_service is not None and user_profile_tracker is not None and ignore_ups is False:
10331028
user_profile_tracker.save_user_profile()
10341029

10351030
return decisions

optimizely/event/user_event_factory.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,7 @@ def create_impression_event(
6767
variation: Optional[Variation] = None
6868
experiment_id = None
6969
if activated_experiment:
70-
# Handle both Experiment objects and holdout dicts
71-
if isinstance(activated_experiment, dict):
72-
experiment_id = activated_experiment.get('id')
73-
else:
74-
experiment_id = activated_experiment.id
70+
experiment_id = activated_experiment.id
7571

7672
if variation_id and flag_key:
7773
# need this condition when we send events involving forced decisions

optimizely/optimizely.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,15 +1252,7 @@ def _create_optimizely_decision(
12521252

12531253
# Create Optimizely Decision Result.
12541254
attributes = user_context.get_user_attributes()
1255-
# Handle both Experiment entities and holdout dicts
1256-
if flag_decision.experiment:
1257-
if isinstance(flag_decision.experiment, dict):
1258-
# Holdout is a dict, not an Experiment entity
1259-
rule_key = flag_decision.experiment.get('key')
1260-
else:
1261-
rule_key = flag_decision.experiment.key
1262-
else:
1263-
rule_key = None
1255+
rule_key = flag_decision.experiment.key if flag_decision.experiment else None
12641256
all_variables = {}
12651257
decision_source = flag_decision.source
12661258
decision_event_dispatched = False

tests/test_decision_service.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1399,8 +1399,8 @@ def test_get_variation_for_feature__returns_variation_for_feature_in_rollout(sel
13991399
self.project_config, feature, user
14001400
)
14011401

1402-
# Assert info log messages were generated (changed from debug due to new flow through get_decision_for_flag)
1403-
self.assertEqual(1, mock_decision_service_logging.info.call_count)
1402+
# Assert debug log messages were generated (rollout decisions log at debug level)
1403+
self.assertEqual(1, mock_decision_service_logging.debug.call_count)
14041404

14051405
def test_get_variation_for_feature__returns_variation_if_user_not_in_experiment_but_in_rollout(
14061406
self,

tests/test_user_context.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -872,8 +872,7 @@ def test_decide__option__include_reasons__feature_rollout(self):
872872
'Evaluating audiences for rule 1: ["11154"].',
873873
'Audiences for rule 1 collectively evaluated to TRUE.',
874874
'User "test_user" meets audience conditions for targeting rule 1.',
875-
'User "test_user" bucketed into a targeting rule 1.',
876-
"The user 'test_user' is bucketed into a rollout for feature flag 'test_feature_in_rollout'."
875+
'User "test_user" bucketed into a targeting rule 1.'
877876
]
878877

879878
self.assertEqual(expected_reasons, actual.reasons)
@@ -1271,8 +1270,7 @@ def test_decide_reasons__hit_everyone_else_rule(self):
12711270
'Evaluating audiences for rule Everyone Else: [].',
12721271
'Audiences for rule Everyone Else collectively evaluated to TRUE.',
12731272
'User "abcde" meets audience conditions for targeting rule Everyone Else.',
1274-
'User "abcde" bucketed into a targeting rule Everyone Else.',
1275-
"The user 'abcde' is bucketed into a rollout for feature flag 'test_feature_in_rollout'."
1273+
'User "abcde" bucketed into a targeting rule Everyone Else.'
12761274
]
12771275

12781276
self.assertEqual(expected_reasons, actual.reasons)

0 commit comments

Comments
 (0)