From 3fab1def959ad674eff2d7ab7dfe8fe447a7eac3 Mon Sep 17 00:00:00 2001 From: Christopher Broderick Date: Thu, 6 Mar 2025 02:37:53 +0000 Subject: [PATCH 1/7] Fix setting the unassigned (-1) option as selected --- helpdesk/templates/helpdesk/filters/kbitems.html | 2 +- helpdesk/templates/helpdesk/filters/owner.html | 2 +- helpdesk/templates/helpdesk/filters/queue.html | 5 +++++ helpdesk/templates/helpdesk/filters/status.html | 9 ++++++++- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/helpdesk/templates/helpdesk/filters/kbitems.html b/helpdesk/templates/helpdesk/filters/kbitems.html index 17cab5f7..87316fea 100644 --- a/helpdesk/templates/helpdesk/filters/kbitems.html +++ b/helpdesk/templates/helpdesk/filters/kbitems.html @@ -8,7 +8,7 @@
{% with magic_number=-1 %} - {% endwith %} diff --git a/helpdesk/templates/helpdesk/filters/queue.html b/helpdesk/templates/helpdesk/filters/queue.html index dd66d53d..4f3b3334 100644 --- a/helpdesk/templates/helpdesk/filters/queue.html +++ b/helpdesk/templates/helpdesk/filters/queue.html @@ -7,6 +7,11 @@
{% for s in status_choices %}{% endfor %} +
From c30bf866df96a8cbc745c7cdb70be4f20c77432a Mon Sep 17 00:00:00 2001 From: Christopher Broderick Date: Thu, 6 Mar 2025 02:38:42 +0000 Subject: [PATCH 2/7] Fix multiple problems with search filters and query building --- helpdesk/query.py | 46 ++++++++++++++++++++++++++++++++++------- helpdesk/views/staff.py | 18 +++++++++++----- 2 files changed, 51 insertions(+), 13 deletions(-) diff --git a/helpdesk/query.py b/helpdesk/query.py index 9ae9e400..1814410c 100644 --- a/helpdesk/query.py +++ b/helpdesk/query.py @@ -28,14 +28,18 @@ def query_from_base64(b64data): def get_search_filter_args(search): + if not search: + return Q() if search.startswith('queue:'): return Q(queue__title__icontains=search[len('queue:'):]) if search.startswith('priority:'): return Q(priority__icontains=search[len('priority:'):]) - filter = Q() + my_filter = Q() for subsearch in search.split("OR"): subsearch = subsearch.strip() - filter = ( + if not subsearch: + continue + my_filter = ( filter | Q(id__icontains=subsearch) | Q(title__icontains=subsearch) | @@ -48,7 +52,7 @@ def get_search_filter_args(search): Q(created__icontains=subsearch) | Q(due_date__icontains=subsearch) ) - return filter + return my_filter DATATABLES_ORDER_COLUMN_CHOICES = Choices( @@ -91,23 +95,49 @@ class __Query__: def __run__(self, queryset): """ - Apply a dict-based set of filters & parameters to a queryset. + Apply a dict-based set of value_filters & parameters to a queryset. queryset is a Django queryset, eg MyModel.objects.all() or MyModel.objects.filter(user=request.user) params is a dictionary that contains the following: - filtering: A dict of Django ORM filters, eg: + filtering: A dict of Django ORM value_filters, eg: {'user__id__in': [1, 3, 103], 'title__contains': 'foo'} search_string: A freetext search string sorting: The name of the column to sort by """ - filter = self.params.get('filtering', {}) - filter_or = self.params.get('filtering_or', {}) + q_args = [] + value_filters = self.params.get('filtering', {}) + null_filters = self.params.get('filtering_null', {}) + if null_filters: + if value_filters: + # Check if any of the value value_filters are for the same field as the + # ISNULL filter so that an OR filter can be set up + matched_null_keys = [] + for null_key in null_filters: + field_path = null_key[:-8] # Chop off the "__isnull" + matched_key = None + for val_key in value_filters: + if val_key.startswith(field_path): + matched_key = val_key + break + if matched_key: + # Remove the matching filters into a Q param + matched_null_keys.append(null_key) + # Create an OR query for the selected value(s) OR if the field is NULL + v = {} + v[val_key] = value_filters[val_key] + n = {} + n[null_key] = null_filters[null_key] + q_args.append((Q(**v) | Q(**n))) + del value_filters[matched_key] + # Now remove the matched mull keys + for null_key in matched_null_keys: + del null_filters[null_key] queryset = queryset.filter( - (Q(**filter) | Q(**filter_or)) & self.get_search_filter_args()) + *q_args, (Q(**value_filters) & Q(**null_filters)) & self.get_search_filter_args()) sorting = self.params.get('sorting', None) if sorting: sortreverse = self.params.get('sortreverse', None) diff --git a/helpdesk/views/staff.py b/helpdesk/views/staff.py index fb6104c0..89aea899 100644 --- a/helpdesk/views/staff.py +++ b/helpdesk/views/staff.py @@ -991,7 +991,7 @@ def ticket_list(request): # a query, to be saved if needed: query_params = { 'filtering': {}, - 'filtering_or': {}, + 'filtering_null': {}, 'sorting': None, 'sortreverse': False, 'search_string': '', @@ -1035,12 +1035,20 @@ def ticket_list(request): for param, filter_command in filter_in_params: if request.GET.get(param) is not None: patterns = request.GET.getlist(param) + if not patterns: + continue + try: + minus_1_ndx = patterns.index("-1") + # Must have the value so remove it and configure to use OR filter on NULL + patterns.pop(minus_1_ndx) + query_params['filtering_null'][filter_null_params[param]] = True + except ValueError: + pass + if not patterns: + # Caters for the case where the filter is only a null filter + continue try: pattern_pks = [int(pattern) for pattern in patterns] - if -1 in pattern_pks: - query_params['filtering_or'][filter_null_params[param]] = True - else: - query_params['filtering_or'][filter_command] = pattern_pks query_params['filtering'][filter_command] = pattern_pks except ValueError: pass From 4ca1ec24dc9c37009dbe83f145023482121cebe2 Mon Sep 17 00:00:00 2001 From: Christopher Broderick Date: Thu, 6 Mar 2025 02:39:49 +0000 Subject: [PATCH 3/7] Fix isnull test --- helpdesk/tests/test_query.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/helpdesk/tests/test_query.py b/helpdesk/tests/test_query.py index bee5b664..f86faf55 100644 --- a/helpdesk/tests/test_query.py +++ b/helpdesk/tests/test_query.py @@ -8,6 +8,7 @@ from helpdesk.tests.helpers import get_staff_user class QueryTests(TestCase): def setUp(self): + # self.maxDiff = None self.queue = Queue.objects.create( title="Test queue", slug="test_queue", @@ -94,7 +95,7 @@ class QueryTests(TestCase): def test_query_by_no_kbitem(self): self.loginUser() query = query_to_base64( - {'filtering_or': {'kbitem__in': [self.kbitem1.pk]}} + {'filtering_null': {'kbitem__isnull': True}} ) response = self.client.get( reverse('helpdesk:datatables_ticket_list', args=[query])) @@ -103,8 +104,8 @@ class QueryTests(TestCase): resp_json, { "data": - [{"ticket": "2 [test_queue-2]", "id": 2, "priority": 3, "title": "assigned to kbitem", "queue": {"title": "Test queue", "id": 1}, "status": "Open", - "created": resp_json["data"][0]["created"], "due_date": None, "assigned_to": "None", "submitter": None, "row_class": "", "time_spent": "", "kbitem": "KBItem 1"}], + [{"ticket": "1 [test_queue-1]", "id": 1, "priority": 3, "title": "unassigned to kbitem", "queue": {"title": "Test queue", "id": 1}, "status": "Open", + "created": resp_json["data"][0]["created"], "due_date": None, "assigned_to": "None", "submitter": None, "row_class": "", "time_spent": "", "kbitem": ""}], "recordsFiltered": 1, "recordsTotal": 1, "draw": 0, From 165adf52308edd6f0186b5ed0dd657cb560914aa Mon Sep 17 00:00:00 2001 From: Christopher Broderick Date: Thu, 6 Mar 2025 02:42:07 +0000 Subject: [PATCH 4/7] Change to support the query fixes so that unassigned option works for persisting visibility of filters --- helpdesk/templates/helpdesk/ticket_list.html | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/helpdesk/templates/helpdesk/ticket_list.html b/helpdesk/templates/helpdesk/ticket_list.html index c511d5f2..58ce7e21 100644 --- a/helpdesk/templates/helpdesk/ticket_list.html +++ b/helpdesk/templates/helpdesk/ticket_list.html @@ -170,13 +170,13 @@ - - - - @@ -200,15 +200,15 @@ id="filterBoxSort"> {% include 'helpdesk/filters/sorting.html' %} -
  • {% include 'helpdesk/filters/owner.html' %}
  • -
  • {% include 'helpdesk/filters/queue.html' %}
  • -
  • {% include 'helpdesk/filters/status.html' %}
  • @@ -220,7 +220,7 @@ id="filterBoxKeywords"> {% include 'helpdesk/filters/keywords.html' %} -
  • {% include 'helpdesk/filters/kbitems.html' %}
  • From e64490e20f6605ae25e472163168c6f44c3ea624 Mon Sep 17 00:00:00 2001 From: Christopher Broderick Date: Thu, 6 Mar 2025 03:12:41 +0000 Subject: [PATCH 5/7] Bump version for a release --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 240d9c85..f725b138 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ from setuptools import find_packages, setup import sys -version = '1.3.0' +version = '1.3.1' # Provided as an attribute, so you can append to these instead From abdd750e2aa0d294f93fc4cd5ac042c0735b4c5b Mon Sep 17 00:00:00 2001 From: Christopher Broderick Date: Thu, 6 Mar 2025 12:18:30 +0000 Subject: [PATCH 6/7] REvert status to not support unassigned since it is a required field. --- helpdesk/templates/helpdesk/filters/status.html | 9 +-------- helpdesk/templates/helpdesk/ticket_list.html | 4 ++-- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/helpdesk/templates/helpdesk/filters/status.html b/helpdesk/templates/helpdesk/filters/status.html index 26cfd2c8..8c2ce03f 100644 --- a/helpdesk/templates/helpdesk/filters/status.html +++ b/helpdesk/templates/helpdesk/filters/status.html @@ -6,14 +6,7 @@
    - +
    diff --git a/helpdesk/templates/helpdesk/ticket_list.html b/helpdesk/templates/helpdesk/ticket_list.html index 58ce7e21..2e644c7f 100644 --- a/helpdesk/templates/helpdesk/ticket_list.html +++ b/helpdesk/templates/helpdesk/ticket_list.html @@ -176,7 +176,7 @@ -