Skip to content

Commit

Permalink
dead code removal
Browse files Browse the repository at this point in the history
  • Loading branch information
khvn26 committed Nov 28, 2023
1 parent eb207b6 commit 5cd1316
Show file tree
Hide file tree
Showing 14 changed files with 59 additions and 397 deletions.
3 changes: 2 additions & 1 deletion api/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import pytest
from django.contrib.contenttypes.models import ContentType
from django.core.cache import cache
from flag_engine.segments.constants import EQUAL
from rest_framework.authtoken.models import Token
from rest_framework.test import APIClient

Expand Down Expand Up @@ -46,7 +47,7 @@
)
from projects.permissions import VIEW_PROJECT
from projects.tags.models import Tag
from segments.models import EQUAL, Condition, Segment, SegmentRule
from segments.models import Condition, Segment, SegmentRule
from task_processor.task_run_method import TaskRunMethod
from users.models import FFAdminUser, UserPermissionGroup

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
from core.constants import INTEGER
from django.core.exceptions import ObjectDoesNotExist
from flag_engine.identities.builders import build_identity_model
from flag_engine.segments.constants import IN
from rest_framework.exceptions import NotFound

from environments.dynamodb import DynamoIdentityWrapper
from environments.identities.models import Identity
from environments.identities.traits.models import Trait
from segments.models import IN, Condition, Segment, SegmentRule
from segments.models import Condition, Segment, SegmentRule
from util.mappers import (
map_environment_to_environment_document,
map_identity_to_identity_document,
Expand Down
18 changes: 8 additions & 10 deletions api/environments/identities/tests/test_models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import pytest
from core.constants import FLOAT
from django.utils import timezone
from flag_engine.segments.constants import (
EQUAL,
GREATER_THAN,
GREATER_THAN_INCLUSIVE,
LESS_THAN_INCLUSIVE,
NOT_EQUAL,
)
from rest_framework.test import APITestCase

from environments.identities.models import Identity
Expand All @@ -15,16 +22,7 @@
from features.value_types import BOOLEAN, INTEGER, STRING
from organisations.models import Organisation
from projects.models import Project
from segments.models import (
EQUAL,
GREATER_THAN,
GREATER_THAN_INCLUSIVE,
LESS_THAN_INCLUSIVE,
NOT_EQUAL,
Condition,
Segment,
SegmentRule,
)
from segments.models import Condition, Segment, SegmentRule

from .helpers import (
create_trait_for_identity,
Expand Down
18 changes: 9 additions & 9 deletions api/environments/identities/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
from unittest.case import TestCase

import pytest
from core.constants import FLAGSMITH_UPDATED_AT_HEADER
from core.constants import FLAGSMITH_UPDATED_AT_HEADER, STRING
from django.test import override_settings
from django.urls import reverse
from django.utils import timezone
from flag_engine.segments.constants import PERCENTAGE_SPLIT
from rest_framework import status
from rest_framework.test import APIClient, APITestCase

Expand All @@ -21,7 +22,6 @@
from integrations.amplitude.models import AmplitudeConfiguration
from organisations.models import Organisation, OrganisationRole
from projects.models import Project
from segments import models
from segments.models import Condition, Segment, SegmentRule
from util.tests import Helper

Expand Down Expand Up @@ -371,7 +371,7 @@ def test_identities_endpoint_returns_traits(self, mock_amplitude_wrapper):
trait = Trait.objects.create(
identity=self.identity,
trait_key="trait_key",
value_type="STRING",
value_type=STRING,
string_value="trait_value",
)

Expand Down Expand Up @@ -423,7 +423,7 @@ def test_identities_endpoint_returns_value_for_segment_if_identity_in_segment(
Trait.objects.create(
identity=self.identity,
trait_key=trait_key,
value_type="STRING",
value_type=STRING,
string_value=trait_value,
)
segment = Segment.objects.create(name="Test Segment", project=self.project)
Expand Down Expand Up @@ -477,7 +477,7 @@ def test_identities_endpoint_returns_value_for_segment_if_identity_in_segment_an
Trait.objects.create(
identity=self.identity,
trait_key=trait_key,
value_type="STRING",
value_type=STRING,
string_value=trait_value,
)
segment = Segment.objects.create(name="Test Segment", project=self.project)
Expand Down Expand Up @@ -529,7 +529,7 @@ def test_identities_endpoint_returns_value_for_segment_if_rule_type_percentage_s
[segment.id, self.identity.id]
)
Condition.objects.create(
operator=models.PERCENTAGE_SPLIT,
operator=PERCENTAGE_SPLIT,
value=(identity_percentage_value + (1 - identity_percentage_value) / 2)
* 100.0,
rule=segment_rule,
Expand Down Expand Up @@ -576,7 +576,7 @@ def test_identities_endpoint_returns_default_value_if_rule_type_percentage_split
[segment.id, self.identity.id]
)
Condition.objects.create(
operator=models.PERCENTAGE_SPLIT,
operator=PERCENTAGE_SPLIT,
value=identity_percentage_value / 2,
rule=segment_rule,
)
Expand Down Expand Up @@ -629,13 +629,13 @@ def test_post_identify_deletes_a_trait_if_trait_value_is_none(self):
trait_1 = Trait.objects.create(
identity=self.identity,
trait_key="trait_key_1",
value_type="STRING",
value_type=STRING,
string_value="trait_value",
)
trait_2 = Trait.objects.create(
identity=self.identity,
trait_key="trait_key_2",
value_type="STRING",
value_type=STRING,
string_value="trait_value",
)

Expand Down
3 changes: 2 additions & 1 deletion api/features/feature_segments/tests/test_models.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import pytest
from core.constants import STRING
from django.test import TestCase
from flag_engine.segments.constants import EQUAL

from environments.identities.models import Identity
from environments.identities.traits.models import Trait
from environments.models import Environment
from features.models import Feature, FeatureSegment
from organisations.models import Organisation
from projects.models import Project
from segments.models import EQUAL, Condition, Segment, SegmentRule
from segments.models import Condition, Segment, SegmentRule


@pytest.mark.django_db
Expand Down
198 changes: 0 additions & 198 deletions api/segments/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,22 @@
import typing
from copy import deepcopy

import semver
from core.constants import BOOLEAN, FLOAT, INTEGER
from core.models import (
AbstractBaseExportableModel,
SoftDeleteExportableModel,
abstract_base_auditable_model_factory,
)
from django.core.exceptions import ValidationError
from django.db import models
from flag_engine.utils.semver import is_semver, remove_semver_suffix
from flag_engine.segments import constants

from audit.constants import SEGMENT_CREATED_MESSAGE, SEGMENT_UPDATED_MESSAGE
from audit.related_object_type import RelatedObjectType
from environments.identities.helpers import (
get_hashed_percentage_for_object_ids,
)
from features.models import Feature
from projects.models import Project

logger = logging.getLogger(__name__)

try:
import re2 as re

logger.info("Using re2 library for regex.")
except ImportError:
logger.warning("Unable to import re2. Falling back to re.")
import re


class Segment(
SoftDeleteExportableModel,
Expand Down Expand Up @@ -127,34 +113,6 @@ def __str__(self):
str(self.segment) if self.segment else str(self.rule),
)

def does_identity_match(
self, identity: "Identity", traits: typing.List["Trait"] = None
) -> bool:
matches_conditions = False
conditions = self.conditions.all()

if conditions.count() == 0:
matches_conditions = True
elif self.type == self.ALL_RULE:
matches_conditions = all(
condition.does_identity_match(identity, traits)
for condition in conditions
)
elif self.type == self.ANY_RULE:
matches_conditions = any(
condition.does_identity_match(identity, traits)
for condition in conditions
)
elif self.type == self.NONE_RULE:
matches_conditions = not any(
condition.does_identity_match(identity, traits)
for condition in conditions
)

return matches_conditions and all(
rule.does_identity_match(identity, traits) for rule in self.rules.all()
)

def get_segment(self):
"""
rules can be a child of a parent rule instead of a segment, this method iterates back up the tree to find the
Expand Down Expand Up @@ -213,162 +171,6 @@ def __str__(self):
self.value,
)

def does_identity_match( # noqa: C901
self, identity: "Identity", traits: typing.List["Trait"] = None
) -> bool:
if self.operator == PERCENTAGE_SPLIT:
return self._check_percentage_split_operator(identity)

# we allow passing in traits to handle when they aren't
# persisted for certain organisations
traits = identity.identity_traits.all() if traits is None else traits
matching_trait = next(
filter(lambda t: t.trait_key == self.property, traits), None
)
if matching_trait is None:
return self.operator == IS_NOT_SET

if self.operator in (IS_SET, IS_NOT_SET):
return self.operator == IS_SET
elif self.operator == MODULO:
if matching_trait.value_type in [INTEGER, FLOAT]:
return self._check_modulo_operator(matching_trait.trait_value)
elif self.operator == IN:
return str(matching_trait.trait_value) in self.value.split(",")
elif matching_trait.value_type == INTEGER:
return self.check_integer_value(matching_trait.integer_value)
elif matching_trait.value_type == FLOAT:
return self.check_float_value(matching_trait.float_value)
elif matching_trait.value_type == BOOLEAN:
return self.check_boolean_value(matching_trait.boolean_value)
elif is_semver(self.value):
return self.check_semver_value(matching_trait.string_value)

return self.check_string_value(matching_trait.string_value)

def _check_percentage_split_operator(self, identity):
try:
float_value = float(self.value) / 100.0
except ValueError:
return False

segment = self.rule.get_segment()
return (
get_hashed_percentage_for_object_ids(
object_ids=[segment.id, identity.get_hash_key()]
)
<= float_value
)

def _check_modulo_operator(self, value: typing.Union[int, float]) -> bool:
try:
divisor, remainder = self.value.split("|")
divisor = float(divisor)
remainder = float(remainder)
except ValueError:
return False

return value % divisor == remainder

def check_integer_value(self, value: int) -> bool:
try:
int_value = int(str(self.value))
except ValueError:
return False

if self.operator == EQUAL:
return value == int_value
elif self.operator == GREATER_THAN:
return value > int_value
elif self.operator == GREATER_THAN_INCLUSIVE:
return value >= int_value
elif self.operator == LESS_THAN:
return value < int_value
elif self.operator == LESS_THAN_INCLUSIVE:
return value <= int_value
elif self.operator == NOT_EQUAL:
return value != int_value

return False

def check_float_value(self, value: float) -> bool:
try:
float_value = float(str(self.value))
except ValueError:
return False

if self.operator == EQUAL:
return value == float_value
elif self.operator == GREATER_THAN:
return value > float_value
elif self.operator == GREATER_THAN_INCLUSIVE:
return value >= float_value
elif self.operator == LESS_THAN:
return value < float_value
elif self.operator == LESS_THAN_INCLUSIVE:
return value <= float_value
elif self.operator == NOT_EQUAL:
return value != float_value

return False

def check_boolean_value(self, value: bool) -> bool:
if self.value in ("False", "false", "0"):
bool_value = False
elif self.value in ("True", "true", "1"):
bool_value = True
else:
return False

if self.operator == EQUAL:
return value == bool_value
elif self.operator == NOT_EQUAL:
return value != bool_value

return False

def check_semver_value(self, value: str) -> bool:
try:
condition_version_info = semver.VersionInfo.parse(
remove_semver_suffix(self.value)
)
except ValueError:
return False

if self.operator == EQUAL:
return value == condition_version_info
elif self.operator == GREATER_THAN:
return value > condition_version_info
elif self.operator == GREATER_THAN_INCLUSIVE:
return value >= condition_version_info
elif self.operator == LESS_THAN:
return value < condition_version_info
elif self.operator == LESS_THAN_INCLUSIVE:
return value <= condition_version_info
elif self.operator == NOT_EQUAL:
return value != condition_version_info

return False

def check_string_value(self, value: str) -> bool:
try:
str_value = str(self.value)
except ValueError:
return False

if self.operator == EQUAL:
return value == str_value
elif self.operator == NOT_EQUAL:
return value != str_value
elif self.operator == CONTAINS:
return str_value in value
elif self.operator == NOT_CONTAINS:
return str_value not in value
elif self.operator == REGEX:
return re.compile(str(self.value)).match(value) is not None

return False

def get_update_log_message(self, history_instance) -> typing.Optional[str]:
return f"Condition updated on segment '{self._get_segment().name}'."

Expand Down
Loading

0 comments on commit 5cd1316

Please sign in to comment.