From 288781140ab24ce1615864405b854e1135c1a1a6 Mon Sep 17 00:00:00 2001 From: David Whittaker Date: Tue, 29 Apr 2025 10:59:25 -0700 Subject: [PATCH 1/2] feat(ui): adding ability to filter out certain case types --- src/dispatch/database/service.py | 35 +++++++++++++++++-- .../dispatch/src/case/TableFilterDialog.vue | 29 ++++++++++++--- .../dispatch/src/case_cost/CaseCostCard.vue | 2 +- 3 files changed, 59 insertions(+), 7 deletions(-) diff --git a/src/dispatch/database/service.py b/src/dispatch/database/service.py index ba326ff4edea..d6db48d0ec18 100644 --- a/src/dispatch/database/service.py +++ b/src/dispatch/database/service.py @@ -116,6 +116,8 @@ def get_named_models(self): return {"IndividualContact"} if model == "TagAll": return {"Tag"} + if model == "NotCaseType": + return {"CaseType"} else: return {self.filter_spec["model"]} return set() @@ -124,6 +126,8 @@ def format_for_sqlalchemy(self, query, default_model): filter_spec = self.filter_spec if filter_spec.get("model") in ["Participant", "Commander", "Assignee"]: filter_spec["model"] = "IndividualContact" + elif filter_spec.get("model") == "NotCaseType": + filter_spec["model"] = "CaseType" elif filter_spec.get("model") == "TagAll": filter_spec["model"] = "Tag" @@ -501,7 +505,7 @@ def common_parameters( ] -def has_tag_all(filter_spec: List[dict]): +def has_filter_model(model: str, filter_spec: List[dict]): """Checks if the filter spec has a TagAll filter.""" if isinstance(filter_spec, list): @@ -511,11 +515,34 @@ def has_tag_all(filter_spec: List[dict]): if key == "and": for condition in value: or_condition = condition.get("or", []) - if or_condition and or_condition[0].get("model") == "TagAll": + if or_condition and or_condition[0].get("model") == model: return True return False +def has_tag_all(filter_spec: List[dict]): + return has_filter_model("TagAll", filter_spec) + + +def has_not_case_type(filter_spec: List[dict]): + return has_filter_model("NotCaseType", filter_spec) + + +def rebuild_filter_spec_for_not_case_type(filter_spec: List[dict]): + new_filter_spec = [] + for key, value in filter_spec.items(): + if key == "and": + for condition in value: + or_condition = condition.get("or", []) + if or_condition and or_condition[0].get("model") == "NotCaseType": + for cond in or_condition: + cond["op"] = "!=" + new_filter_spec.append({"and": [{"and": [cond]}]}) + else: + new_filter_spec.append(condition) + return {"and": new_filter_spec} + + def rebuild_filter_spec_without_tag_all(filter_spec: List[dict]): """Rebuilds the filter spec without the TagAll filter.""" new_filter_spec = [] @@ -565,6 +592,10 @@ def search_filter_sort_paginate( query = apply_filter_specific_joins(model_cls, filter_spec, query) # if the filter_spec has the TagAll filter, we need to split the query up # and intersect all of the results + if has_not_case_type(filter_spec): + new_filter_spec = rebuild_filter_spec_for_not_case_type(filter_spec) + if new_filter_spec: + query = apply_filters(query, new_filter_spec, model_cls) if has_tag_all(filter_spec): new_filter_spec, tag_all_spec = rebuild_filter_spec_without_tag_all(filter_spec) if new_filter_spec: diff --git a/src/dispatch/static/dispatch/src/case/TableFilterDialog.vue b/src/dispatch/static/dispatch/src/case/TableFilterDialog.vue index d7acb07e6c16..4dac0dc74590 100644 --- a/src/dispatch/static/dispatch/src/case/TableFilterDialog.vue +++ b/src/dispatch/static/dispatch/src/case/TableFilterDialog.vue @@ -21,7 +21,23 @@ - + + +
+ + {{ + exclude_mode ? "Exclude selected" : "Include only selected" + }} +
+
@@ -102,7 +118,12 @@ const props = defineProps({ const display = ref(false) const local_case_priority = ref([]) const local_case_severity = ref([]) -const local_case_type = ref([]) +const local_selected_case_types = ref([]) +const exclude_mode = ref(false) // Default to "include only selected" mode +const local_case_type = computed(() => (!exclude_mode.value ? local_selected_case_types.value : [])) +const local_not_case_type = computed(() => + exclude_mode.value ? local_selected_case_types.value : [] +) const local_closed_at = ref({}) const local_project = ref(props.projects) const local_reported_at = ref({}) @@ -118,7 +139,6 @@ const case_priority = computed( const case_severity = computed( () => store.state.case_management.table.options.filters.case_severity ) -const case_type = computed(() => store.state.case_management.table.options.filters.case_type) const project = computed(() => store.state.case_management.table.options.filters.project) const status = computed(() => store.state.case_management.table.options.filters.status) const tag = computed(() => store.state.case_management.table.options.filters.tag) @@ -128,7 +148,7 @@ const numFilters = computed(() => { return sum([ case_priority.value?.length || 0, case_severity.value?.length || 0, - case_type.value?.length || 0, + local_selected_case_types.value?.length || 0, project.value?.length || 0, status.value?.length || 0, tag.value?.length || 0, @@ -152,6 +172,7 @@ const applyFilters = () => { case_priority: local_case_priority.value, case_severity: local_case_severity.value, case_type: local_case_type.value, + not_case_type: local_not_case_type.value, closed_at: local_closed_at.value, project: local_project.value, reported_at: local_reported_at.value, diff --git a/src/dispatch/static/dispatch/src/case_cost/CaseCostCard.vue b/src/dispatch/static/dispatch/src/case_cost/CaseCostCard.vue index abff2cb07ed5..476738c24c74 100644 --- a/src/dispatch/static/dispatch/src/case_cost/CaseCostCard.vue +++ b/src/dispatch/static/dispatch/src/case_cost/CaseCostCard.vue @@ -72,7 +72,7 @@ export default { computed: { totalCost: function () { var totalCost = this.caseCosts.reduce(function (accumulator, item) { - if (item.case_cost_type.model_type == "New") { + if (item.case_cost_type?.model_type == "New") { return accumulator } return accumulator + item.amount From 3a672b9f0fe9cd3b12a8fc51be58b2f36b611de4 Mon Sep 17 00:00:00 2001 From: David Whittaker Date: Tue, 29 Apr 2025 11:04:43 -0700 Subject: [PATCH 2/2] fixing type declaration --- src/dispatch/database/service.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dispatch/database/service.py b/src/dispatch/database/service.py index d6db48d0ec18..7c4fd7b03a92 100644 --- a/src/dispatch/database/service.py +++ b/src/dispatch/database/service.py @@ -528,7 +528,7 @@ def has_not_case_type(filter_spec: List[dict]): return has_filter_model("NotCaseType", filter_spec) -def rebuild_filter_spec_for_not_case_type(filter_spec: List[dict]): +def rebuild_filter_spec_for_not_case_type(filter_spec: dict): new_filter_spec = [] for key, value in filter_spec.items(): if key == "and": @@ -543,7 +543,7 @@ def rebuild_filter_spec_for_not_case_type(filter_spec: List[dict]): return {"and": new_filter_spec} -def rebuild_filter_spec_without_tag_all(filter_spec: List[dict]): +def rebuild_filter_spec_without_tag_all(filter_spec: dict): """Rebuilds the filter spec without the TagAll filter.""" new_filter_spec = [] tag_all_spec = []