-
Notifications
You must be signed in to change notification settings - Fork 54
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great! thank you! I have only a few notes, and a tiny commit that adds a test I would like to see passing. I'm happy to address them, if you like.
@kflemin, how to you want to handle that white space in the ui @ebeers-png mentioned above?
@ebeers-png, Have you noticed any different in query speed?
seed/models/data_views.py
Outdated
or_views = set(cycle.propertyview_set.filter(labels__in=or_labels)) | ||
views = set.intersection(views or or_views, or_views) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe I'm missing something, but wouldn't this suffice?
or_views = set(cycle.propertyview_set.filter(labels__in=or_labels)) | |
views = set.intersection(views or or_views, or_views) | |
views = views.filter(labels__in=or_labels) |
seed/models/data_views.py
Outdated
exclude_views = set(cycle.propertyview_set.exclude(labels__in=exclude_labels)) | ||
views = set.intersection(views or exclude_views, exclude_views) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here
exclude_views = set(cycle.propertyview_set.exclude(labels__in=exclude_labels)) | |
views = set.intersection(views or exclude_views, exclude_views) | |
views = views.exclude(labels__in=exclude_labels) |
seed/models/data_views.py
Outdated
views_all = [] | ||
for label in labels: | ||
for label in and_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)) | ||
views = set.intersection(*map(set, views_all)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would this suffice?
views = views.filter(**{"labels__in": l for l in and_label})
const and_label_ids = []; | ||
const or_label_ids = []; | ||
const exclude_label_ids = []; | ||
for (const label of $scope.selected_and_labels) { | ||
and_label_ids.push(label.id); | ||
} | ||
for (const label of $scope.selected_or_labels) { | ||
or_label_ids.push(label.id); | ||
} | ||
for (const label of $scope.selected_exclude_labels) { | ||
exclude_label_ids.push(label.id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const and_label_ids = []; | |
const or_label_ids = []; | |
const exclude_label_ids = []; | |
for (const label of $scope.selected_and_labels) { | |
and_label_ids.push(label.id); | |
} | |
for (const label of $scope.selected_or_labels) { | |
or_label_ids.push(label.id); | |
} | |
for (const label of $scope.selected_exclude_labels) { | |
exclude_label_ids.push(label.id); | |
const and_label_ids = scope.selected_and_labels.map(l => l.id); | |
const or_label_ids = scope.selected_or_labels.map(l => l.id); | |
const exclude_label_ids = scope.selected_exclude_labels.map(l => l.id); |
const current_and_labels = []; | ||
const current_or_labels = []; | ||
const current_exclude_labels = []; | ||
for (const label of $scope.selected_and_labels) { | ||
current_and_labels.push(label.id); | ||
} | ||
const saved_labels = $scope.currentFilterGroup.labels; | ||
const current_label_logic = $scope.labelLogic; | ||
const saved_label_logic = $scope.currentFilterGroup.label_logic; | ||
for (const label of $scope.selected_or_labels) { | ||
current_or_labels.push(label.id); | ||
} | ||
for (const label of $scope.selected_exclude_labels) { | ||
current_exclude_labels.push(label.id); | ||
} | ||
saved_and_labels = $scope.currentFilterGroup.and_labels; | ||
saved_or_labels = $scope.currentFilterGroup.or_labels; | ||
saved_exclude_labels = $scope.currentFilterGroup.exclude_labels; | ||
if (!_.isEqual(current_filters, saved_filters)) { | ||
$scope.Modified = true; | ||
} else if (!_.isEqual(current_labels.sort(), saved_labels.sort())) { | ||
} else if (!_.isEqual(current_and_labels.sort(), saved_and_labels.sort()) | ||
|| !_.isEqual(current_or_labels.sort(), saved_or_labels.sort()) | ||
|| !_.isEqual(current_exclude_labels.sort(), saved_exclude_labels.sort())) { | ||
$scope.Modified = true; | ||
} else { | ||
$scope.Modified = current_label_logic !== saved_label_logic; | ||
$scope.Modified = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const current_and_labels = []; | |
const current_or_labels = []; | |
const current_exclude_labels = []; | |
for (const label of $scope.selected_and_labels) { | |
current_and_labels.push(label.id); | |
} | |
const saved_labels = $scope.currentFilterGroup.labels; | |
const current_label_logic = $scope.labelLogic; | |
const saved_label_logic = $scope.currentFilterGroup.label_logic; | |
for (const label of $scope.selected_or_labels) { | |
current_or_labels.push(label.id); | |
} | |
for (const label of $scope.selected_exclude_labels) { | |
current_exclude_labels.push(label.id); | |
} | |
saved_and_labels = $scope.currentFilterGroup.and_labels; | |
saved_or_labels = $scope.currentFilterGroup.or_labels; | |
saved_exclude_labels = $scope.currentFilterGroup.exclude_labels; | |
if (!_.isEqual(current_filters, saved_filters)) { | |
$scope.Modified = true; | |
} else if (!_.isEqual(current_labels.sort(), saved_labels.sort())) { | |
} else if (!_.isEqual(current_and_labels.sort(), saved_and_labels.sort()) | |
|| !_.isEqual(current_or_labels.sort(), saved_or_labels.sort()) | |
|| !_.isEqual(current_exclude_labels.sort(), saved_exclude_labels.sort())) { | |
$scope.Modified = true; | |
} else { | |
$scope.Modified = current_label_logic !== saved_label_logic; | |
$scope.Modified = false; | |
current_and_labels = new Set($scope.selected_and_labels.map(l => l.id)); | |
current_or_labels = new Set($scope.selected_and_labels.map(l => l.id)); | |
current_exclude_labels = new Set($scope.selected_and_labels.map(l => l.id)); | |
saved_and_labels = new Set($scope.currentFilterGroup.and_labels); | |
saved_or_labels = new Set($scope.currentFilterGroup.or_labels); | |
saved_exclude_labels = new Set($scope.currentFilterGroup.exclude_labels); | |
$scope.Modified = !( | |
_.isEqual(current_and_labels, saved_and_labels) && | |
_.isEqual(current_or_labels, saved_or_labels) && | |
_.isEqual(current_exclude_labels, saved_exclude_labels) | |
) |
seed/views/v3/filter_group.py
Outdated
if "and_labels" in request.data: | ||
filter_group.and_labels.set(request.data["and_labels"]) | ||
if "or_labels" in request.data: | ||
filter_group.or_labels.set(request.data["or_labels"]) | ||
if "exclude_labels" in request.data: | ||
filter_group.exclude_labels.set(request.data["exclude_labels"]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will this catch erroneous data?
@@ -0,0 +1,40 @@ | |||
# Generated by Django 3.2.18 on 2023-12-19 16:15 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💯
@haneslinger Thank you for the review and suggestions! Added the catch for erroneous data to the update method, and I was able to simplify _get_label_views() a little more as well. Re: query speed, I haven't really noticed a difference in inventory load times. But that's mainly because SEED calculates included/excluded building ids on the frontend, in inventory_list_controller (lines 1110-1128). That's not the case for the custom report insights though, which uses _get_label_views() in models/data_views to get the right buildings. So I'd have to check whether the old code (with |
The failing test is not this PRs fault. I'm addressing it now |
What's this PR do?
Adds more complex label filtering logic to the inventory list, by allowing users to filter the inventory by label on AND, OR, and EXCLUDE all at once.
The complex label logic can be saved to filter groups, and migrations will update pre-existing filter groups to the new setup.
Remaining work
Before merging this into develop, the inventory list menu should be re-organized to make room for the two new rows, which take up a lot of screen space.
The inventory map page, which also uses label filtering, was left unchanged, since it doesn't (yet) use Filter Groups and will work fine without the complex label logic. But it can be easily updated if needed, since its own code is already based on the inventory list's.
What are the relevant tickets?
#3371
Screenshots (if appropriate)
(Ignore the missing icons)