From 320ae7b4c9fbba9c01b5e21ac802a66ef436fbd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Kl=C3=BCnder?= Date: Fri, 10 Nov 2023 18:59:37 +0100 Subject: [PATCH] add RangingBeacon to mapdata --- src/c3nav/editor/api.py | 9 +- src/c3nav/editor/forms.py | 2 +- src/c3nav/editor/urls.py | 1 + src/c3nav/editor/views/edit.py | 2 +- .../mapdata/migrations/0087_rangingbeacon.py | 84 +++++++++++++++++++ src/c3nav/mapdata/models/geometry/space.py | 37 +++++++- 6 files changed, 130 insertions(+), 5 deletions(-) create mode 100644 src/c3nav/mapdata/migrations/0087_rangingbeacon.py diff --git a/src/c3nav/editor/api.py b/src/c3nav/editor/api.py index 5dc506ea..93949a92 100644 --- a/src/c3nav/editor/api.py +++ b/src/c3nav/editor/api.py @@ -147,6 +147,7 @@ class EditorViewSet(EditorViewSetMixin, ViewSet): Door = request.changeset.wrap_model('Door') LocationGroup = request.changeset.wrap_model('LocationGroup') WifiMeasurement = request.changeset.wrap_model('WifiMeasurement') + RangingBeacon = request.changeset.wrap_model('RangingBeacon') level = request.GET.get('level') space = request.GET.get('space') @@ -179,6 +180,7 @@ class EditorViewSet(EditorViewSetMixin, ViewSet): Prefetch('spaces__holes', Hole.objects.only('geometry', 'space')), Prefetch('spaces__altitudemarkers', AltitudeMarker.objects.only('geometry', 'space')), Prefetch('spaces__wifi_measurements', WifiMeasurement.objects.only('geometry', 'space')), + Prefetch('spaces__ranging_beacons', RangingBeacon.objects.only('geometry', 'space')), # Prefetch('spaces__graphnodes', graphnodes_qs) ) @@ -212,7 +214,8 @@ class EditorViewSet(EditorViewSetMixin, ViewSet): self._get_level_geometries(level), *(self._get_level_geometries(level) for level in levels_on_top), *(space.altitudemarkers.all() for space in level.spaces.all()), - *(space.wifi_measurements.all() for space in level.spaces.all()) + *(space.wifi_measurements.all() for space in level.spaces.all()), + *(space.ranging_beacons.all() for space in level.spaces.all()), # graphedges, # graphnodes, ) @@ -228,7 +231,7 @@ class EditorViewSet(EditorViewSetMixin, ViewSet): if request.user_permissions.can_access_base_mapdata: doors = [door for door in level.doors.filter(Door.q_for_request(request)).all() - if door.geometry.intersects(space.geometry)] + if door.geometry.wrapped_geom.intersects(space.geometry.wrapped_geom)] doors_space_geom = unary_union( [unwrap_geom(door.geometry) for door in doors] + [unwrap_geom(space.geometry)] @@ -325,6 +328,7 @@ class EditorViewSet(EditorViewSetMixin, ViewSet): space.columns.all().only('geometry', 'space'), space.altitudemarkers.all().only('geometry', 'space'), space.wifi_measurements.all().only('geometry', 'space'), + space.ranging_beacons.all().only('geometry', 'space'), space.pois.filter(POI.q_for_request(request)).only('geometry', 'space').prefetch_related( Prefetch('groups', LocationGroup.objects.only( 'color', 'category', 'priority', 'hierarchy', 'category__priority', 'category__allow_pois' @@ -370,6 +374,7 @@ class EditorViewSet(EditorViewSetMixin, ViewSet): 'graphedge': '#00CC00', 'altitudemarker': '#0000FF', 'wifimeasurement': '#DDDD00', + 'rangingbeacon': '#CC00CC', }) @action(detail=False, methods=['get']) diff --git a/src/c3nav/editor/forms.py b/src/c3nav/editor/forms.py index 4b705c66..a0a27f7e 100644 --- a/src/c3nav/editor/forms.py +++ b/src/c3nav/editor/forms.py @@ -279,7 +279,7 @@ class EditorFormBase(I18nModelFormMixin, ModelForm): def create_editor_form(editor_model): possible_fields = ['slug', 'name', 'title', 'title_plural', 'help_text', 'position_secret', - 'icon', 'join_edges', 'up_separate', + 'icon', 'join_edges', 'up_separate', 'bssid', 'walk', 'ordering', 'category', 'width', 'groups', 'height', 'color', 'priority', 'hierarchy', 'icon_name', 'base_altitude', 'waytype', 'access_restriction', 'default_height', 'door_height', 'outside', 'can_search', 'can_describe', 'geometry', 'single', 'altitude', 'short_label', diff --git a/src/c3nav/editor/urls.py b/src/c3nav/editor/urls.py index 276880d2..48c2a032 100644 --- a/src/c3nav/editor/urls.py +++ b/src/c3nav/editor/urls.py @@ -78,3 +78,4 @@ urlpatterns.extend(add_editor_urls('AltitudeMarker', 'Space')) urlpatterns.extend(add_editor_urls('LeaveDescription', 'Space')) urlpatterns.extend(add_editor_urls('CrossDescription', 'Space')) urlpatterns.extend(add_editor_urls('WifiMeasurement', 'Space')) +urlpatterns.extend(add_editor_urls('RangingBeacon', 'Space')) diff --git a/src/c3nav/editor/views/edit.py b/src/c3nav/editor/views/edit.py index 2d46c6fa..575e458f 100644 --- a/src/c3nav/editor/views/edit.py +++ b/src/c3nav/editor/views/edit.py @@ -106,7 +106,7 @@ def space_detail(request, level, pk): if edit_utils.can_access_child_base_mapdata: submodels = ('POI', 'Area', 'Obstacle', 'LineObstacle', 'Stair', 'Ramp', 'Column', 'Hole', 'AltitudeMarker', 'LeaveDescription', 'CrossDescription', - 'WifiMeasurement') + 'WifiMeasurement', 'RangingBeacon') else: submodels = ('POI', 'Area', 'AltitudeMarker', 'LeaveDescription', 'CrossDescription') diff --git a/src/c3nav/mapdata/migrations/0087_rangingbeacon.py b/src/c3nav/mapdata/migrations/0087_rangingbeacon.py new file mode 100644 index 00000000..0b309751 --- /dev/null +++ b/src/c3nav/mapdata/migrations/0087_rangingbeacon.py @@ -0,0 +1,84 @@ +# Generated by Django 4.2.1 on 2023-11-10 17:53 + +import c3nav.mapdata.fields +from decimal import Decimal +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ("mapdata", "0086_django_4_0"), + ] + + operations = [ + migrations.CreateModel( + name="RangingBeacon", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "import_tag", + models.CharField( + blank=True, max_length=32, null=True, verbose_name="import tag" + ), + ), + ( + "geometry", + c3nav.mapdata.fields.GeometryField(default=None, geomtype="point"), + ), + ( + "bssid", + models.CharField( + max_length=17, + unique=True, + validators=[ + django.core.validators.RegexValidator( + code="invalid_bssid", + message="Must be a lower-case bssid", + regex="^([a-f0-9]{2}:){5}[a-f0-9]{2}$", + ) + ], + verbose_name="BSSID", + ), + ), + ( + "altitude", + models.DecimalField( + decimal_places=2, + default=0, + max_digits=6, + validators=[ + django.core.validators.MinValueValidator(Decimal("0")) + ], + verbose_name="altitude above ground", + ), + ), + ( + "comment", + models.TextField(blank=True, null=True, verbose_name="comment"), + ), + ( + "space", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="mapdata.space", + verbose_name="space", + ), + ), + ], + options={ + "verbose_name": "Ranging beacon", + "verbose_name_plural": "Ranging beacons", + "default_related_name": "ranging_beacons", + }, + ), + ] diff --git a/src/c3nav/mapdata/models/geometry/space.py b/src/c3nav/mapdata/models/geometry/space.py index 8eeba7c7..956e1a5a 100644 --- a/src/c3nav/mapdata/models/geometry/space.py +++ b/src/c3nav/mapdata/models/geometry/space.py @@ -2,7 +2,7 @@ from decimal import Decimal from django.conf import settings from django.core.exceptions import ObjectDoesNotExist -from django.core.validators import MinValueValidator +from django.core.validators import MinValueValidator, RegexValidator from django.db import models from django.urls import reverse from django.utils.functional import cached_property @@ -398,3 +398,38 @@ class WifiMeasurement(SpaceGeometryMixin, models.Model): @property def geometry_changed(self): return False + + +class RangingBeacon(SpaceGeometryMixin, models.Model): + """ + A ranging beacon + """ + geometry = GeometryField('point') + bssid = models.CharField(_('BSSID'), max_length=17, unique=True, + validators=[RegexValidator( + regex='^([a-f0-9]{2}:){5}[a-f0-9]{2}$', + message='Must be a lower-case bssid', + code='invalid_bssid' + )]) + altitude = models.DecimalField(_('altitude above ground'), max_digits=6, decimal_places=2, default=0, + validators=[MinValueValidator(Decimal('0'))]) + comment = models.TextField(null=True, blank=True, verbose_name=_('comment')) + + class Meta: + verbose_name = _('Ranging beacon') + verbose_name_plural = _('Ranging beacons') + default_related_name = 'ranging_beacons' + + @property + def all_geometry_changed(self): + return False + + @property + def geometry_changed(self): + return False + + @property + def title(self): + if self.comment: + return f'{self.bssid} ({self.comment})' + return self.bssid