Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Labels: Added complex filtering logic #4449

Merged
merged 13 commits into from
Feb 6, 2024
28 changes: 28 additions & 0 deletions seed/migrations/0212_add_filtergroup_labels.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Generated by Django 3.2.18 on 2023-12-19 16:15

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('seed', '0211_auto_20240109_1348'),
]

operations = [
migrations.AddField(
model_name='filtergroup',
name='and_labels',
field=models.ManyToManyField(related_name='and_filter_groups', to='seed.StatusLabel'),
),
migrations.AddField(
model_name='filtergroup',
name='exclude_labels',
field=models.ManyToManyField(related_name='exclude_filter_groups', to='seed.StatusLabel'),
),
migrations.AddField(
model_name='filtergroup',
name='or_labels',
field=models.ManyToManyField(related_name='or_filter_groups', to='seed.StatusLabel'),
),
]
43 changes: 43 additions & 0 deletions seed/migrations/0213_move_filtergroup_labels.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Generated by Django 3.2.18 on 2023-12-19 16:15

from django.db import migrations


def move_filter_groups(apps, schema_editor):
FilterGroup = apps.get_model('seed', 'FilterGroup')
for filter_group in FilterGroup.objects.all():
if filter_group.labels.exists() and filter_group.label_logic == 0: # and
filter_group.and_labels.set(filter_group.labels.all())
elif filter_group.labels.exists() and filter_group.label_logic == 1: # or
filter_group.or_labels.set(filter_group.labels.all())
elif filter_group.labels.exists() and filter_group.label_logic == 2: # exclude
filter_group.exclude_labels.set(filter_group.labels.all())


def move_filter_groups_back(apps, schema_editor):
# if the filter group only has one set of labels, we can move those back
FilterGroup = apps.get_model('seed', 'FilterGroup')
for filter_group in FilterGroup.objects.all():
if filter_group.and_labels.exists() and not (filter_group.or_labels.exists() or filter_group.exclude_labels.exists()):
filter_group.labels.set(filter_group.and_labels.all())
filter_group.label_logic = 0
filter_group.save()
elif filter_group.or_labels.exists() and not (filter_group.and_labels.exists() or filter_group.exclude_labels.exists()):
filter_group.labels.set(filter_group.or_labels.all())
filter_group.label_logic = 1
filter_group.save()
elif filter_group.exclude_labels.exists() and not (filter_group.and_labels.exists() or filter_group.or_labels.exists()):
filter_group.labels.set(filter_group.exclude_labels.all())
filter_group.label_logic = 2
filter_group.save()


class Migration(migrations.Migration):

dependencies = [
('seed', '0212_add_filtergroup_labels'),
]

operations = [
migrations.RunPython(move_filter_groups, move_filter_groups_back)
]
21 changes: 21 additions & 0 deletions seed/migrations/0214_delete_filtergroup_labels.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Generated by Django 3.2.18 on 2023-12-19 16:20

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('seed', '0213_move_filtergroup_labels'),
]

operations = [
migrations.RemoveField(
model_name='filtergroup',
name='label_logic',
),
migrations.RemoveField(
model_name='filtergroup',
name='labels',
),
]
31 changes: 16 additions & 15 deletions seed/models/data_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
from seed.models.columns import Column
from seed.models.cycles import Cycle
from seed.models.filter_group import FilterGroup
from seed.models.models import StatusLabel as Label
from seed.models.properties import PropertyState, PropertyView
from seed.utils.search import build_view_filters_and_sorts

Expand Down Expand Up @@ -243,22 +242,24 @@ def _combine_views(self, filter_views, label_views):
return list(filter_views)

def _get_label_views(self, cycle, filter_group):
if len(filter_group.labels.all()) == 0:
if not (filter_group.and_labels.exists() or filter_group.or_labels.exists() or filter_group.exclude_labels.exists()):
return None

logic = filter_group.label_logic
labels = Label.objects.filter(id__in=filter_group.labels.all())

if logic == 0: # and
views_all = []
for label in labels:
views = cycle.propertyview_set.filter(labels__in=[label])
views_all.append(views)
return list(set.intersection(*map(set, views_all)))
elif logic == 1: # or
return list(cycle.propertyview_set.filter(labels__in=labels))
elif logic == 2: # exclude
return list(cycle.propertyview_set.exclude(labels__in=labels))
and_labels = filter_group.and_labels.all()
or_labels = filter_group.or_labels.all()
exclude_labels = filter_group.exclude_labels.all()
views = None
if and_labels.exists(): # and
views = views or cycle.propertyview_set.all()
for label in and_labels:
views = views.filter(labels=label)
if or_labels.exists(): # or
views = views or cycle.propertyview_set.all()
views = views.filter(labels__in=or_labels)
if exclude_labels.exists(): # exclude
views = views or cycle.propertyview_set.all()
views = views.exclude(labels__in=exclude_labels)
return list(views)

def _get_filter_group_views(self, cycle, query_dict):
org_id = self.organization.id
Expand Down
12 changes: 3 additions & 9 deletions seed/models/filter_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,16 @@
)
from seed.models.models import StatusLabel

AND = 0
LABEL_LOGIC_TYPE = [
(AND, 'and'),
(1, 'or'),
(2, 'exclude'),
]


class FilterGroup(models.Model):

name = models.CharField(max_length=255)
organization = models.ForeignKey(Organization, on_delete=models.CASCADE, related_name='filter_groups', null=False)
inventory_type = models.IntegerField(choices=VIEW_LIST_INVENTORY_TYPE, default=VIEW_LIST_PROPERTY)
query_dict = models.JSONField(null=False, default=dict)
labels = models.ManyToManyField(StatusLabel)
label_logic = models.IntegerField(choices=LABEL_LOGIC_TYPE, default=AND)
and_labels = models.ManyToManyField(StatusLabel, related_name='and_filter_groups')
or_labels = models.ManyToManyField(StatusLabel, related_name='or_filter_groups')
exclude_labels = models.ManyToManyField(StatusLabel, related_name='exclude_filter_groups')

class Meta:
ordering = ['id']
Expand Down
8 changes: 4 additions & 4 deletions seed/serializers/filter_groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@
from rest_framework import serializers

from seed.models import VIEW_LIST_INVENTORY_TYPE, FilterGroup
from seed.models.filter_group import LABEL_LOGIC_TYPE


class FilterGroupSerializer(serializers.ModelSerializer):
class Meta:
model = FilterGroup
fields = ('name', 'query_dict', 'inventory_type', 'id', "organization_id", "labels", "label_logic")
fields = ('name', 'query_dict', 'inventory_type', 'id', "organization_id", "and_labels", "or_labels", "exclude_labels")
extra_kwargs = {
'user': {'read_only': True},
'organization': {'read_only': True},
Expand All @@ -24,7 +23,8 @@ def to_representation(self, instance):
ret = super().to_representation(instance)

ret["inventory_type"] = VIEW_LIST_INVENTORY_TYPE[ret["inventory_type"]][1]
ret["label_logic"] = LABEL_LOGIC_TYPE[ret["label_logic"]][1]
ret["labels"] = sorted(ret["labels"])
ret["and_labels"] = sorted(ret["and_labels"])
ret["or_labels"] = sorted(ret["or_labels"])
ret["exclude_labels"] = sorted(ret["exclude_labels"])

return ret
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@ angular.module('BE.seed.controller.filter_group_modal', []).controller('filter_g
name: $scope.newName,
query_dict: $scope.data.query_dict,
inventory_type: $scope.data.inventory_type,
labels: $scope.data.labels,
label_logic: $scope.data.label_logic
and_labels: $scope.data.and_labels,
or_labels: $scope.data.or_labels,
exclude_labels: $scope.data.exclude_labels,
})
.then((result) => {
$uibModalInstance.close(result);
Expand Down
Loading
Loading