From 98fa484e07e119510f1194673f0dd6961905824a Mon Sep 17 00:00:00 2001 From: Aksiznarf-Uar Date: Mon, 16 Dec 2024 15:35:43 +0100 Subject: [PATCH 1/3] Filtering now refreshes as you type. --- src/nac/subviews/device_management.py | 14 +++++- src/static/device_filtering.js | 30 +++++++++++ src/templates/base.html | 5 +- src/templates/devices.html | 72 +++------------------------ 4 files changed, 55 insertions(+), 66 deletions(-) create mode 100644 src/static/device_filtering.js diff --git a/src/nac/subviews/device_management.py b/src/nac/subviews/device_management.py index 6afad44..94d23e3 100644 --- a/src/nac/subviews/device_management.py +++ b/src/nac/subviews/device_management.py @@ -4,6 +4,8 @@ from django.urls import reverse_lazy from django.shortcuts import render import json +from django.http import JsonResponse +from django.template.loader import render_to_string from ..models import Device, AuthorizationGroup, DeviceRoleProd from ..forms import DeviceForm, DeviceSearchForm @@ -38,8 +40,18 @@ def get_queryset(self): device_list = device_list.filter(appl_NAC_DeviceRoleProd__in=selected_device_roles_prod) return device_list.order_by("name") + def get(self, request, *args, **kwargs): + # Check if the request is an AJAX request + if request.headers.get('X-Requested-With') == 'XMLHttpRequest': + # Handle AJAX request by rendering only the relevant part of the template + html = render_to_string('devices_results.html', {"device_list": self.get_queryset()}) + return JsonResponse({'html': html}) + + # Otherwise, handle a normal HTTP request + return super().get(request, *args, **kwargs) + # we need this for the drop-down menus with filtering options - def get_context_data(self, *, object_list=None, **kwargs): + def get_context_data(self, **kwargs): context = super(DeviceListView, self).get_context_data(**kwargs) context["auth_group_list"] = AuthorizationGroup.objects.filter(id__in=self.request.user.authorization_group.all()) context["device_role_prod_list"] = DeviceRoleProd.objects.all() diff --git a/src/static/device_filtering.js b/src/static/device_filtering.js new file mode 100644 index 0000000..8dec349 --- /dev/null +++ b/src/static/device_filtering.js @@ -0,0 +1,30 @@ +const search_form = $("#search_form") +const results_div = $('#results') +const endpoint = '/devices/' +const delay_by_in_ms = 700 +let scheduled_function = false + +let ajax_call = function (endpoint, request_parameters) { + $.getJSON(endpoint, request_parameters) + .done(response => { + // replace the HTML contents + results_div.html(response['html']) + }) +} + +search_form.on('input', function () { + + const request_parameters = { + search_string: $('#id_search_string').val(), + device_role_prod: $('#id_device_role_prod').val(), + authorization_group: $('#id_authorization_group').val() + } + + // if scheduled_function is NOT false, cancel the execution of the function + if (scheduled_function) { + clearTimeout(scheduled_function) + } + + // setTimeout returns the ID of the function to be executed + scheduled_function = setTimeout(ajax_call, delay_by_in_ms, endpoint, request_parameters); +}) \ No newline at end of file diff --git a/src/templates/base.html b/src/templates/base.html index 8e98d94..052952b 100644 --- a/src/templates/base.html +++ b/src/templates/base.html @@ -15,7 +15,10 @@ - + + + + {% block head %}{% endblock %} diff --git a/src/templates/devices.html b/src/templates/devices.html index c764758..66ce164 100644 --- a/src/templates/devices.html +++ b/src/templates/devices.html @@ -5,7 +5,7 @@ {% block content %}

Devices

-
+
{% for field in search_form %}
@@ -15,68 +15,12 @@

Devices

- - - - - - - - - - - - {% for device in device_list %} - - - - - - - - - {% endfor %} -
NameStatusFQDNDevice Role ProdMAC
- {{ device.name }} - - {% if device.appl_NAC_Active %} - appl_NAC_Active_True - {% else %} - appl_NAC_Active_False - {% endif %} - {% if device.appl_NAC_Install %} - appl_NAC_Install - {% endif %} - - {{ device.appl_NAC_FQDN }} - - {{ device.appl_NAC_DeviceRoleProd }} - - {% for mac in device.get_appl_NAC_macAddressAIR %} - - {% if mac != None %} - appl_NAC_macAddressAIR - {{mac}}
- {% endif %} -
- {% endfor %} - {% for mac in device.get_appl_NAC_macAddressCAB %} - - {% if mac != None %} - appl_NAC_macAddressCAB - {{mac}}
- {% endif %} -
- {% endfor %} -
- - edit - - - - delete - -
+
+ {% include "devices_results.html" %} +
-{% endblock content %} + + + +{% endblock content %} \ No newline at end of file From 93c78b83e39e253750cabf08e01c9a47b90daa2d Mon Sep 17 00:00:00 2001 From: Aksiznarf-Uar Date: Thu, 19 Dec 2024 15:50:41 +0100 Subject: [PATCH 2/3] Added devices_results.html --- src/templates/devices_results.html | 66 ++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 src/templates/devices_results.html diff --git a/src/templates/devices_results.html b/src/templates/devices_results.html new file mode 100644 index 0000000..de390e6 --- /dev/null +++ b/src/templates/devices_results.html @@ -0,0 +1,66 @@ +{% load static %} + + + + + + + + + + + + + + {% for device in device_list %} + + + + + + + + + {% endfor %} +
NameStatusFQDNDevice Role ProdMAC
+ {{ device.name }} + + {% if device.appl_NAC_Active %} + appl_NAC_Active_True + {% else %} + appl_NAC_Active_False + {% endif %} + {% if device.appl_NAC_Install %} + appl_NAC_Install + {% endif %} + + {{ device.appl_NAC_FQDN }} + + {{ device.appl_NAC_DeviceRoleProd }} + + {% for mac in device.get_appl_NAC_macAddressAIR %} + + {% if mac != None %} + appl_NAC_macAddressAIR + {{mac}}
+ {% endif %} +
+ {% endfor %} + {% for mac in device.get_appl_NAC_macAddressCAB %} + + {% if mac != None %} + appl_NAC_macAddressCAB + {{mac}}
+ {% endif %} +
+ {% endfor %} +
+ + edit + + + + delete + +
+
\ No newline at end of file From fb2aec0100d27dee6aefc0dd3b98da12d6cb6098 Mon Sep 17 00:00:00 2001 From: Aksiznarf-Uar Date: Thu, 9 Jan 2025 17:32:19 +0100 Subject: [PATCH 3/3] Added test --- src/tests/test_views/test_device_page.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/tests/test_views/test_device_page.py b/src/tests/test_views/test_device_page.py index 48df91e..bb3d804 100644 --- a/src/tests/test_views/test_device_page.py +++ b/src/tests/test_views/test_device_page.py @@ -33,6 +33,28 @@ def test_device_search(query, result): assertQuerySetEqual(desired_qs, result_qs, ordered=False) +@pytest.mark.django_db +def test_result_rendering(client): + test_user = CustomUser.objects.create(name="test") + test_user.set_password("test") + test_user.authorization_group.set([AuthorizationGroup.objects.get(pk=1)]) + test_user.save() + + client.force_login(test_user) + + url = reverse_lazy('devices') + response = client.get(url) + assert response.status_code == 200 + + ajax_response = client.get( + url, # The URL where the AJAX request is sent + {"search_string": "", "authorization_group": "", "device_role_prod": ""}, # Parameters to be sent in the AJAX request + HTTP_X_REQUESTED_WITH='XMLHttpRequest' # Indicate it's an AJAX request + ) + + assert ajax_response.status_code == 200 + + @pytest.mark.django_db @pytest.mark.parametrize("auth_group, device_role_prod, result", [("", "", [1, 2, 3, 4, 5]),