From e66a04ccf0b6cb161b208ce574d1d2886ee4c3fb Mon Sep 17 00:00:00 2001 From: Andrew Aikman Date: Thu, 15 Feb 2024 08:28:11 +0000 Subject: [PATCH] Working drop down and table rebuild with updated endpoints --- shedpi_hub_dashboard/admin.py | 2 +- shedpi_hub_dashboard/serlializers.py | 1 + .../static/shedpi_hub_dashboard/js/index.js | 73 +++++++++++++------ .../templates/shedpi_hub_dashboard/index.html | 1 + shedpi_hub_dashboard/tests/test_endpoints.py | 68 ++++++++++++++++- shedpi_hub_dashboard/views.py | 27 +++++++ 6 files changed, 147 insertions(+), 25 deletions(-) diff --git a/shedpi_hub_dashboard/admin.py b/shedpi_hub_dashboard/admin.py index 66ff110..25e6ea9 100644 --- a/shedpi_hub_dashboard/admin.py +++ b/shedpi_hub_dashboard/admin.py @@ -15,4 +15,4 @@ class DeviceModuleAdmin(admin.ModelAdmin): @admin.register(DeviceModuleReading) class DeviceModuleReadingAdmin(admin.ModelAdmin): - pass + list_display = ("id", "device_module_id", "created_at") diff --git a/shedpi_hub_dashboard/serlializers.py b/shedpi_hub_dashboard/serlializers.py index 3107c42..85590a5 100644 --- a/shedpi_hub_dashboard/serlializers.py +++ b/shedpi_hub_dashboard/serlializers.py @@ -13,3 +13,4 @@ class DeviceModuleReadingSerializer(serializers.ModelSerializer): class Meta: model = DeviceModuleReading fields = "__all__" + extra_kwargs = {"device_module": {"required": True}} diff --git a/shedpi_hub_dashboard/static/shedpi_hub_dashboard/js/index.js b/shedpi_hub_dashboard/static/shedpi_hub_dashboard/js/index.js index 88ed0ea..0c1f5fd 100644 --- a/shedpi_hub_dashboard/static/shedpi_hub_dashboard/js/index.js +++ b/shedpi_hub_dashboard/static/shedpi_hub_dashboard/js/index.js @@ -1,8 +1,17 @@ -const contents = document.getElementsByClassName("contents"); -let section = contents[0] +const contents = document.getElementsByClassName("contents")[0]; +let section = contents +let deviceModuleEndpoint = "/api/v1/device-module-readings/" + +// Global store for the device modules, with schema +let storeDeviceModules = [] +let deviceModuleSchemaMap = {} /* Drop down selection */ +// Create dropdown container +const deviceModuleSelectorContainer = document.createElement("div"); +section.append(deviceModuleSelectorContainer); + const urlDeviceModule = "/api/v1/device-module/" let endpointDeviceModule = new Request(urlDeviceModule); response = fetch(endpointDeviceModule) @@ -14,10 +23,15 @@ response = fetch(endpointDeviceModule) return response.json(); }) .then((response) => { - drawDropdown(response) + storeDeviceModules = response + drawDropdown() + + // Build schema map + }); -let drawDropdown = function (data) { +let drawDropdown = function () { + let data = storeDeviceModules let dropdown = document.createElement("select"); // Table Header @@ -25,7 +39,6 @@ let drawDropdown = function (data) { emptySelector.textContent = "Please Select" dropdown.append(emptySelector) - dropdown.addEventListener('change', function (e) { optionId = this.selectedOptions[0].id @@ -39,26 +52,35 @@ let drawDropdown = function (data) { let optionElement = document.createElement("option"); optionElement.textContent = deviceModule.device + " - " + deviceModule.name - optionElement.id = deviceModule.device + optionElement.id = deviceModule.id + + // Build schema map + deviceModuleSchemaMap[deviceModule.id] = deviceModule.schema dropdown.append(optionElement); } // Add the drpdown to the page - section.append(dropdown); + deviceModuleSelectorContainer.append(dropdown); }; /* Table visual */ -let loadTableData = function (deviceModuleId) { +// Create table container +const tableContainer = document.createElement("div"); +section.append(tableContainer); +let loadTableData = function (deviceModuleId) { // TODO: Get data based on deviceModuleId // Endpoint: http://127.0.0.1:8000/api/v1/device-module-readings/ // device_module = deviceModuleId + // FIXME: Build the query string using the js libraries + const endpoint = deviceModuleEndpoint + "?device_module=" + deviceModuleId + // FIXME: Pass a reversed full url through + // const urlDeviceModuleReading = section.getAttribute("data-json-feed") + let endpointDeviceModuleReading = new Request(endpoint); - const urlDeviceModuleReading = section.getAttribute("data-json-feed") - let endpointDeviceModuleReading = new Request(urlDeviceModuleReading); response = fetch(endpointDeviceModuleReading) .then((response) => { if (!response.ok) { @@ -74,36 +96,45 @@ let loadTableData = function (deviceModuleId) { let drawTable = function (data) { + // First empty the table container + tableContainer.textContent = "" + let table = document.createElement("table"); // Table Header let headerRow = document.createElement("tr"); - for (let heading in data.headings) { + // TODO: Build the header rows from the data, for now use the first row. + // For other projects this was supplied in the endpoint + // Could use the schema, what about historic data that may violate it, + // we only validate this when historic data is updated + // Built as a ist because the pagination would hammer the device modiule + + let deviceModules = storeDeviceModules + + + console.log("Drawing table with data: ", data) + + for (let heading in data[0]) { let headerItem = document.createElement("th"); - headerItem.textContent = data.headings[heading] + headerItem.textContent = heading headerRow.append(headerItem); } table.append(headerRow); // Table Contents - for (let row in data.readings) { + for (let row in data) { let contentRow = document.createElement("tr"); - for (let reading in data.readings[row]) { + for (let reading in data[row]) { let contentItem = document.createElement("td"); - contentItem.textContent = data.readings[row][reading] + contentItem.textContent = data[row][reading] contentRow.append(contentItem); } table.append(contentRow); } // Add the table to the page - section.append(table); + tableContainer.append(table); } - - -/* TODO - On select click, load the table with the reading data */ -// Bind dropdown click -// reloadTable: destroy and rebuild diff --git a/shedpi_hub_dashboard/templates/shedpi_hub_dashboard/index.html b/shedpi_hub_dashboard/templates/shedpi_hub_dashboard/index.html index 9fd0115..087edc9 100644 --- a/shedpi_hub_dashboard/templates/shedpi_hub_dashboard/index.html +++ b/shedpi_hub_dashboard/templates/shedpi_hub_dashboard/index.html @@ -15,6 +15,7 @@

Shed Pi data

+
diff --git a/shedpi_hub_dashboard/tests/test_endpoints.py b/shedpi_hub_dashboard/tests/test_endpoints.py index 5890130..dd9f333 100644 --- a/shedpi_hub_dashboard/tests/test_endpoints.py +++ b/shedpi_hub_dashboard/tests/test_endpoints.py @@ -4,7 +4,10 @@ from django.urls import reverse from rest_framework import status -from shedpi_hub_dashboard.tests.utils.factories import DeviceModuleFactory +from shedpi_hub_dashboard.tests.utils.factories import ( + DeviceModuleFactory, + DeviceModuleReadingFactory, +) @pytest.mark.django_db @@ -19,9 +22,68 @@ def test_device_module_list(client): # TODO: device_module_readings_list + + +@pytest.mark.django_db +def test_device_module_readings_list(client): + """ + An individual device module readings are returned from the module readings endpoint + """ + schema = { + "$id": "https://example.com/person.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Reading", + "type": "object", + "properties": { + "temperature": {"type": "string", "description": "The Temperature"}, + }, + } + device_module = DeviceModuleFactory(schema=schema) + reading_1 = DeviceModuleReadingFactory( + device_module=device_module, data={"temperature": "20"} + ) + reading_2 = DeviceModuleReadingFactory( + device_module=device_module, data={"temperature": "22"} + ) + # Another modules readings that shouldn't be returned + DeviceModuleReadingFactory(data={"temperature": "10"}) + + # url = reverse("devicemodulereading-detail", kwargs={"pk": device_module.id}) + url = reverse("devicemodulereading-list") + response = client.get(url, data={"device_module": device_module.id}) + + assert response.status_code == status.HTTP_200_OK + assert len(response.data) == 2 + + @pytest.mark.django_db -def device_module_readings_list(client): - assert False +def test_device_module_readings_list_no_device_module_supplied(client): + """ """ + schema = { + "$id": "https://example.com/person.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Reading", + "type": "object", + "properties": { + "temperature": {"type": "string", "description": "The Temperature"}, + }, + } + device_module = DeviceModuleFactory(schema=schema) + reading_1 = DeviceModuleReadingFactory( + device_module=device_module, data={"temperature": "20"} + ) + reading_2 = DeviceModuleReadingFactory( + device_module=device_module, data={"temperature": "22"} + ) + # Another modules readings that shouldn't be returned + DeviceModuleReadingFactory(data={"temperature": "10"}) + + # url = reverse("devicemodulereading-detail", kwargs={"pk": device_module.id}) + url = reverse("devicemodulereading-list") + response = client.get(url, data={}) + + assert response.status_code == status.HTTP_200_OK + assert len(response.data) == 3 @pytest.mark.django_db diff --git a/shedpi_hub_dashboard/views.py b/shedpi_hub_dashboard/views.py index 5a2cf4c..6396412 100644 --- a/shedpi_hub_dashboard/views.py +++ b/shedpi_hub_dashboard/views.py @@ -18,3 +18,30 @@ class DeviceModuleViewSet(viewsets.ModelViewSet): class DeviceModuleReadingViewSet(viewsets.ModelViewSet): queryset = DeviceModuleReading.objects.all() serializer_class = DeviceModuleReadingSerializer + + def get_queryset(self): + # FIXME: Validate that the user supplied this get param! + device_module_id = self.request.query_params.get("device_module") + + if device_module_id: + return self.queryset.filter(device_module=device_module_id) + + return self.queryset + + # def list(self, request): + # queryset = self.get_queryset() + # + # context = {"request": request} + # device_module_id = self.request.query_params.get("device_module") + # + # if device_module_id: + # queryset = queryset.filter(device_module=device_module_id) + # + # context["device_module"] = device_module_id + # + # context["queryset"] = queryset + # + # serializer = self.get_serializer(data=request.data, context=context) + # serializer.is_valid(raise_exception=True) + # + # return Response(serializer.data)