diff --git a/helpdesk/query.py b/helpdesk/query.py index 9ae9e400..9c5a0930 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 null 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/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 @@
@@ -200,11 +200,11 @@ id="filterBoxSort"> {% include 'helpdesk/filters/sorting.html' %} -
  • {% include 'helpdesk/filters/owner.html' %}
  • -
  • {% include 'helpdesk/filters/queue.html' %}
  • @@ -220,7 +220,7 @@ id="filterBoxKeywords"> {% include 'helpdesk/filters/keywords.html' %} -
  • {% include 'helpdesk/filters/kbitems.html' %}
  • 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, 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 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