| 67 | |
| 68 | |
| 69 | class FeatureStateModel(BaseModel, validate_assignment=True): |
| 70 | feature: FeatureModel |
| 71 | enabled: bool |
| 72 | django_id: typing.Optional[int] = None |
| 73 | feature_segment: typing.Optional[FeatureSegmentModel] = None |
| 74 | featurestate_uuid: UUID4 = Field(default_factory=uuid.uuid4) |
| 75 | feature_state_value: typing.Any = None |
| 76 | multivariate_feature_state_values: MultivariateFeatureStateValueList = Field( |
| 77 | default_factory=MultivariateFeatureStateValueList |
| 78 | ) |
| 79 | |
| 80 | def set_value(self, value: typing.Any) -> None: |
| 81 | self.feature_state_value = value |
| 82 | |
| 83 | def get_value(self, identity_id: typing.Union[None, int, str] = None) -> typing.Any: |
| 84 | """ |
| 85 | Get the value of the feature state. |
| 86 | |
| 87 | :param identity_id: a unique identifier for the identity, can be either a |
| 88 | numeric id or a string but must be unique for the identity. |
| 89 | :return: the value of the feature state. |
| 90 | """ |
| 91 | if identity_id and len(self.multivariate_feature_state_values) > 0: |
| 92 | return self._get_multivariate_value(identity_id) |
| 93 | return self.feature_state_value |
| 94 | |
| 95 | def _get_multivariate_value( |
| 96 | self, identity_id: typing.Union[int, str] |
| 97 | ) -> typing.Any: |
| 98 | percentage_value = get_hashed_percentage_for_object_ids( |
| 99 | [self.django_id or str(self.featurestate_uuid), identity_id] |
| 100 | ) |
| 101 | |
| 102 | # Iterate over the mv options in order of id (so we get the same value each |
| 103 | # time) to determine the correct value to return to the identity based on |
| 104 | # the percentage allocations of the multivariate options. This gives us a |
| 105 | # way to ensure that the same value is returned every time we use the same |
| 106 | # percentage value. |
| 107 | start_percentage = 0.0 |
| 108 | |
| 109 | def _mv_fs_sort_key(mv_value: MultivariateFeatureStateValueModel) -> SupportsLt: |
| 110 | return mv_value.id or mv_value.mv_fs_value_uuid |
| 111 | |
| 112 | for mv_value in sorted( |
| 113 | self.multivariate_feature_state_values, |
| 114 | key=_mv_fs_sort_key, |
| 115 | ): |
| 116 | limit = mv_value.percentage_allocation + start_percentage |
| 117 | if start_percentage <= percentage_value < limit: |
| 118 | return mv_value.multivariate_feature_option.value |
| 119 | |
| 120 | start_percentage = limit |
| 121 | |
| 122 | # default to return the control value if no MV values found, although this |
| 123 | # should never happen |
| 124 | return self.feature_state_value # pragma: no cover |
no outgoing calls
searching dependent graphs…