Skip to content

Commit

Permalink
Merge pull request #185 from ambitioninc/master
Browse files Browse the repository at this point in the history
back merge master into develop
  • Loading branch information
somewes authored Nov 9, 2023
2 parents c975fbd + 453e54a commit a92a635
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 27 deletions.
2 changes: 2 additions & 0 deletions entity/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

LOGIC_STRING_OPERATORS = {'&', '|', '!'}
17 changes: 13 additions & 4 deletions entity/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from python3_utils import compare_on_attr
from functools import reduce

from entity.constants import LOGIC_STRING_OPERATORS
from entity.exceptions import InvalidLogicStringException


Expand Down Expand Up @@ -454,18 +455,25 @@ def _process_kmatch(self, kmatch, full_set):
Every item is 2 elements - the operator and the value or list of values
"""
entity_ids = set()
operators = {'&', '|', '!'}

if isinstance(kmatch, set):
return kmatch

if len(kmatch) == 2 and kmatch[0] not in operators:
# We can always assume operator + list where the list is either sets or another operator + list
if len(kmatch) != 2 or kmatch[0] not in LOGIC_STRING_OPERATORS:
return kmatch

# Apply the operator to the rest of the sets
if kmatch[0] == '&':
entity_ids = self._process_kmatch(kmatch[1][0], full_set) & self._process_kmatch(kmatch[1][1], full_set)
# Add the first element to the set
entity_ids.update(self._process_kmatch(kmatch[1][0], full_set))
for next_element in kmatch[1][1:]:
entity_ids &= self._process_kmatch(next_element, full_set)
elif kmatch[0] == '|':
entity_ids = self._process_kmatch(kmatch[1][0], full_set) | self._process_kmatch(kmatch[1][1], full_set)
# Add the first element to the set
entity_ids.update(self._process_kmatch(kmatch[1][0], full_set))
for next_element in kmatch[1][1:]:
entity_ids |= self._process_kmatch(next_element, full_set)
elif kmatch[0] == '!':
entity_ids = full_set - self._process_kmatch(kmatch[1], full_set)

Expand Down Expand Up @@ -526,6 +534,7 @@ def get_all_entities(self, membership_cache=None, entities_by_kind=None, return_
def get_entity_ids_from_logic_string(self, entities_by_kind, memberships):
entity_kind_id = memberships[0][1]
full_set = set(entities_by_kind[entity_kind_id]['all'])

try:
filter_tree = ast.parse(self.logic_string.lower())
except:
Expand Down
77 changes: 55 additions & 22 deletions entity/tests/model_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -831,44 +831,77 @@ def test_logic_string(self):
def test_logic_string_not(self):
"""
Verifies that the universal set is properly fetched and used to NOT a set
Group A: 0, 1, 2
NOT(A) = 3, 4, 5, 6, 7, 8
Location A: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
Role A: 0, 1
Role B: 2, 3
Role C: 4, 5
Role D: 6, 7
Memberships:
1. User in Group A
1. Accounts in Location A
2. Accounts in Role A
3. Accounts in Role B
4. Accounts in Role C
5. Accounts in Role D
Logic: 1 AND NOT(2 OR 3 OR 4 OR 5)
Breakdown:
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) AND NOT((0, 1) OR (2, 3) OR (4, 5) OR (6, 7))
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) AND NOT(0, 1, 2, 3, 4, 5, 6, 7)
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) AND (8, 9)
(8, 9)
"""
logic_string = '1 AND NOT(2 OR 3 OR 4 OR 5)'

# Create entity kinds
location_kind = G(EntityKind)
role_kind = G(EntityKind)
account_kind = G(EntityKind)

location_a = G(Entity, entity_kind=location_kind)
role_a = G(Entity, entity_kind=role_kind)
role_b = G(Entity, entity_kind=role_kind)
role_c = G(Entity, entity_kind=role_kind)
role_d = G(Entity, entity_kind=role_kind)

Logic: NOT(1)
(3, 4, 5, 6, 7, 8)
"""
super_entity_kind = G(EntityKind)
sub_entity_kind = G(EntityKind)
super_entity_a = G(Entity, entity_kind=super_entity_kind)
sub_entities = [
G(Entity, entity_kind=sub_entity_kind)
G(Entity, entity_kind=account_kind)
for _ in range(10)
]

# Create the relationships
# Create the role relationships
relationships = [
EntityRelationship(sub_entity=sub_entities[0], super_entity=super_entity_a),
EntityRelationship(sub_entity=sub_entities[1], super_entity=super_entity_a),
EntityRelationship(sub_entity=sub_entities[2], super_entity=super_entity_a),
EntityRelationship(sub_entity=sub_entities[0], super_entity=role_a),
EntityRelationship(sub_entity=sub_entities[1], super_entity=role_a),
EntityRelationship(sub_entity=sub_entities[2], super_entity=role_b),
EntityRelationship(sub_entity=sub_entities[3], super_entity=role_b),
EntityRelationship(sub_entity=sub_entities[4], super_entity=role_c),
EntityRelationship(sub_entity=sub_entities[5], super_entity=role_c),
EntityRelationship(sub_entity=sub_entities[6], super_entity=role_d),
EntityRelationship(sub_entity=sub_entities[7], super_entity=role_d),
]

# Create location relationships
for sub_entity in sub_entities:
relationships.append(EntityRelationship(sub_entity=sub_entity, super_entity=location_a))

EntityRelationship.objects.bulk_create(relationships)

# Create the entity group
entity_group = G(EntityGroup, logic_string='NOT(1)')
entity_group = G(EntityGroup, logic_string=logic_string)

# Create the membership
G(EntityGroupMembership, entity_group=entity_group, sub_entity_kind=sub_entity_kind, entity=super_entity_a)
# Create the memberships
G(
EntityGroupMembership,
entity_group=entity_group, sub_entity_kind=account_kind, entity=location_a, sort_order=1
)
G(EntityGroupMembership, entity_group=entity_group, sub_entity_kind=account_kind, entity=role_a, sort_order=2)
G(EntityGroupMembership, entity_group=entity_group, sub_entity_kind=account_kind, entity=role_b, sort_order=3)
G(EntityGroupMembership, entity_group=entity_group, sub_entity_kind=account_kind, entity=role_c, sort_order=4)
G(EntityGroupMembership, entity_group=entity_group, sub_entity_kind=account_kind, entity=role_d, sort_order=5)

entity_ids = entity_group.get_all_entities()
self.assertEqual(entity_ids, set([
sub_entities[3].id,
sub_entities[4].id,
sub_entities[5].id,
sub_entities[6].id,
sub_entities[7].id,
sub_entities[8].id,
sub_entities[9].id,
]))
Expand Down
2 changes: 1 addition & 1 deletion entity/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '6.2.0'
__version__ = '6.2.2'
4 changes: 4 additions & 0 deletions release_notes.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
## Release Notes

- 6.2.2:
- Fixed entity group logic string for logic sets containing more than 2 items being operated on
- 6.2.1:
- Rebumped because publish messed up
- 6.2.0:
- Add support for boolean logic strings to apply to entity group memberships
- 6.1.1:
Expand Down

0 comments on commit a92a635

Please sign in to comment.