Skip to content

Commit e1e9295

Browse files
committed
[feature] Add API endpoint for indoor map coordinates #828
Implemented API to return device coordinates for a given location ID. Fixes #828
1 parent 85eee35 commit e1e9295

File tree

4 files changed

+79
-0
lines changed

4 files changed

+79
-0
lines changed

openwisp_controller/geo/api/views.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ class Meta(OrganizationManagedFilter.Meta):
5151
model = FloorPlan
5252

5353

54+
class DeviceFloorplanCoordinatesFilter(OrganizationManagedFilter):
55+
class Meta(OrganizationManagedFilter.Meta):
56+
model = Location
57+
fields = OrganizationManagedFilter.Meta.fields + ["floorplan__floor"]
58+
59+
5460
class ListViewPagination(pagination.PageNumberPagination):
5561
page_size = 10
5662
page_size_query_param = "page_size"
@@ -186,6 +192,38 @@ class GeoJsonLocationList(
186192
filterset_class = LocationOrganizationFilter
187193

188194

195+
class DeviceFloorplanCoordinatesList(ProtectedAPIMixin, generics.ListAPIView):
196+
"""
197+
List coordinates of device floorplan for a given location ID
198+
"""
199+
200+
serializer_class = DeviceLocationSerializer
201+
pagination_class = ListViewPagination
202+
filter_backends = [filters.DjangoFilterBackend]
203+
filterset_class = DeviceFloorplanCoordinatesFilter
204+
queryset = DeviceLocation.objects.select_related(
205+
"content_object", "location", "floorplan"
206+
)
207+
208+
def get_queryset(self):
209+
location_id = self.kwargs.get("pk")
210+
floor = self.request.query_params.get("floor")
211+
queryset = super().get_queryset().filter(location_id=location_id)
212+
if floor:
213+
queryset = queryset.filter(floorplan__floor=floor)
214+
return queryset
215+
216+
def list(self, request, *args, **kwargs):
217+
queryset = self.get_queryset()
218+
serializer = self.get_serializer(queryset, many=True)
219+
available_floors = queryset.values_list(
220+
"floorplan__floor", flat=True
221+
).distinct()
222+
return Response(
223+
{"devices": serializer.data, "available_floors": list(available_floors)}
224+
)
225+
226+
189227
class LocationDeviceList(
190228
FilterByParentManaged, ProtectedAPIMixin, generics.ListAPIView
191229
):
@@ -238,6 +276,7 @@ class LocationDetailView(
238276
# add with_geo filter to device API
239277
DeviceListCreateView.filterset_class = DeviceListFilter
240278

279+
device_floorplan_coordinates = DeviceFloorplanCoordinatesList.as_view()
241280
device_coordinates = DeviceCoordinatesView.as_view()
242281
device_location = DeviceLocationView.as_view()
243282
geojson = GeoJsonLocationList.as_view()

openwisp_controller/geo/tests/test_api.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,3 +1033,30 @@ def test_deactivated_device(self):
10331033
with self.subTest("Test deleting DeviceLocation"):
10341034
response = self.client.delete(url)
10351035
self.assertEqual(response.status_code, 403)
1036+
1037+
def test_floorplan_coordinates(self):
1038+
org = self._create_org()
1039+
location = self._create_location(type="indoor", organization=org)
1040+
floor1 = self._create_floorplan(floor=1, location=location)
1041+
floor2 = self._create_floorplan(floor=2, location=location)
1042+
device1 = self._create_device(
1043+
name="device1", mac_address="00:00:00:00:00:01", organization=org
1044+
)
1045+
device2 = self._create_device(
1046+
name="device2", mac_address="00:00:00:00:00:02", organization=org
1047+
)
1048+
self._create_object_location(
1049+
content_object=device1,
1050+
location=location,
1051+
floorplan=floor1,
1052+
organization=org,
1053+
)
1054+
self._create_object_location(
1055+
content_object=device2,
1056+
location=location,
1057+
floorplan=floor2,
1058+
organization=org,
1059+
)
1060+
path = reverse("geo_api:device_floorplan_coordinates", args=[location.id])
1061+
response = self.client.get(path)
1062+
print(response.__dict__)

openwisp_controller/geo/utils.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,9 @@ def get_geo_urls(geo_views):
4141
geo_views.detail_location,
4242
name="detail_location",
4343
),
44+
path(
45+
"api/v1/controller/location/<str:pk>/floorplan/devices/",
46+
geo_views.device_floorplan_coordinates,
47+
name="device_floorplan_coordinates",
48+
),
4449
]

tests/openwisp2/sample_geo/views.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
from openwisp_controller.geo.api.views import (
22
DeviceCoordinatesView as BaseDeviceCoordinatesView,
33
)
4+
from openwisp_controller.geo.api.views import (
5+
DeviceFloorplanCoordinatesList as BaseDeviceFloorplanCoordinatesList,
6+
)
47
from openwisp_controller.geo.api.views import (
58
DeviceLocationView as BaseDeviceLocationView,
69
)
@@ -56,6 +59,11 @@ class LocationDetailView(BaseLocationDetailView):
5659
pass
5760

5861

62+
class DeviceFloorplanCoordinatesList(BaseDeviceFloorplanCoordinatesList):
63+
pass
64+
65+
66+
device_floorplan_coordinates = DeviceFloorplanCoordinatesList.as_view()
5967
device_coordinates = DeviceCoordinatesView.as_view()
6068
device_location = DeviceLocationView.as_view()
6169
geojson = GeoJsonLocationList.as_view()

0 commit comments

Comments
 (0)