From da4cc419ca2c8794bc2f873767e5c102b25b3c70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Kl=C3=BCnder?= Date: Fri, 27 Dec 2024 22:05:19 +0100 Subject: [PATCH] introduce ap_name and implement bssid_from_scans_to_beacons --- src/c3nav/editor/forms.py | 2 +- .../commands/bssid_from_scans_to_beacons.py | 12 +++++++ .../migrations/0134_rangingbeacon_ap_name.py | 36 +++++++++++++++++++ src/c3nav/mapdata/models/geometry/space.py | 26 +++++++++++--- src/c3nav/mapdata/quests/positioning.py | 6 ++-- 5 files changed, 74 insertions(+), 8 deletions(-) create mode 100644 src/c3nav/mapdata/management/commands/bssid_from_scans_to_beacons.py create mode 100644 src/c3nav/mapdata/migrations/0134_rangingbeacon_ap_name.py diff --git a/src/c3nav/editor/forms.py b/src/c3nav/editor/forms.py index 8d2fa519..a7db5b13 100644 --- a/src/c3nav/editor/forms.py +++ b/src/c3nav/editor/forms.py @@ -384,7 +384,7 @@ def create_editor_form(editor_model): 'ordering', 'category', 'width', 'groups', 'height', 'color', 'in_legend', 'priority', 'hierarchy', 'icon_name', 'base_altitude', 'intermediate', 'waytype', 'access_restriction', 'edit_access_restriction', 'default_height', 'door_height', 'outside', 'identifyable', 'can_search', 'can_describe', 'geometry', 'single', 'altitude', - 'level_index', 'short_label', 'origin_space', 'target_space', 'data', 'comment', 'slow_down_factor', + 'level_index', 'short_label', 'origin_space', 'target_space', 'data', "ap_name", 'comment', 'slow_down_factor', 'groundaltitude', 'node_number', 'wifi_bssids', 'bluetooth_address', 'group', 'ibeacon_uuid', 'ibeacon_major', 'ibeacon_minor', 'uwb_address', 'extra_seconds', 'speed', 'can_report_missing', 'can_report_mistake', 'description', 'speed_up', 'description_up', 'avoid_by_default', 'report_help_text', 'enter_description', diff --git a/src/c3nav/mapdata/management/commands/bssid_from_scans_to_beacons.py b/src/c3nav/mapdata/management/commands/bssid_from_scans_to_beacons.py new file mode 100644 index 00000000..e1f9680e --- /dev/null +++ b/src/c3nav/mapdata/management/commands/bssid_from_scans_to_beacons.py @@ -0,0 +1,12 @@ +from django.core.management.base import BaseCommand +from django.db import transaction + +from c3nav.mapdata.models.geometry.space import BeaconMeasurement + + +class Command(BaseCommand): + help = 'collect BSSIDS for AP names from measurements' + + def handle(self, *args, **options): + with transaction.atomic(): + BeaconMeasurement.contribute_bssid_to_beacons(BeaconMeasurement.objects.all()) \ No newline at end of file diff --git a/src/c3nav/mapdata/migrations/0134_rangingbeacon_ap_name.py b/src/c3nav/mapdata/migrations/0134_rangingbeacon_ap_name.py new file mode 100644 index 00000000..07dd6b2f --- /dev/null +++ b/src/c3nav/mapdata/migrations/0134_rangingbeacon_ap_name.py @@ -0,0 +1,36 @@ +# Generated by Django 5.0.8 on 2024-12-27 20:46 + +from django.db import migrations, models + + +def fill_ap_name(apps, schema_editor): + RangingBeacon = apps.get_model('mapdata', 'rangingbeacon') + for ranging_beacon in RangingBeacon.objects.filter(import_tag__startswith='noc:'): + ranging_beacon.ap_name = ranging_beacon.import_tag[4:] + if ranging_beacon.comment == ranging_beacon.import_tag[4:]: + ranging_beacon.comment = None + ranging_beacon.save() + + +def unfill_ap_name(apps, schema_editor): + RangingBeacon = apps.get_model('mapdata', 'rangingbeacon') + for ranging_beacon in RangingBeacon.objects.filter(ap_name__isnull=False, import_tag__startswith='noc:'): + if ranging_beacon.ap_name == ranging_beacon.import_tag[4:]: + ranging_beacon.comment = ' '.join(((ranging_beacon.comment or ''), ranging_beacon.ap_name)).strip() + ranging_beacon.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('mapdata', '0133_beaconmeasurement_fill_quest'), + ] + + operations = [ + migrations.AddField( + model_name='rangingbeacon', + name='ap_name', + field=models.TextField(blank=True, null=True, verbose_name='AP name'), + ), + migrations.RunPython(fill_ap_name, unfill_ap_name), + ] diff --git a/src/c3nav/mapdata/models/geometry/space.py b/src/c3nav/mapdata/models/geometry/space.py index f38ae720..d833939e 100644 --- a/src/c3nav/mapdata/models/geometry/space.py +++ b/src/c3nav/mapdata/models/geometry/space.py @@ -444,7 +444,7 @@ class BeaconMeasurement(SpaceGeometryMixin, models.Model): verbose_name=_('author')) comment = models.TextField(null=True, blank=True, verbose_name=_('comment')) data: BeaconMeasurementDataSchema = SchemaField(BeaconMeasurementDataSchema, - verbose_name=_('Measurement list'), + verbose_name=_('Measurement list'), default=BeaconMeasurementDataSchema()) fill_quest = models.BooleanField(_('create a quest to fill this'), default=False) @@ -462,6 +462,22 @@ class BeaconMeasurement(SpaceGeometryMixin, models.Model): def geometry_changed(self): return False + @staticmethod + def contribute_bssid_to_beacons(items: list["BeaconMeasurement"]): + map_name = {} + for item in items: + for scan in item.data.wifi: + for peer in scan: + if peer.ap_name: + map_name.setdefault(peer.ap_name, []).append(peer.bssid) + for beacon in RangingBeacon.objects.filter(ap_name__in=map_name.keys()): + beacon.wifi_bssids = list(set(beacon.wifi_bssids) | set(map_name[beacon.ap_name])) + beacon.save() + + def save(self, *args, **kwargs): + self.contribute_bssid_to_beacons([self]) + return super().save(*args, **kwargs) + class RangingBeacon(SpaceGeometryMixin, models.Model): """ @@ -498,6 +514,7 @@ class RangingBeacon(SpaceGeometryMixin, models.Model): altitude = models.DecimalField(_('altitude above ground'), max_digits=6, decimal_places=2, default=0, validators=[MinValueValidator(Decimal('0'))]) + ap_name = models.TextField(null=True, blank=True, verbose_name=_('AP name')) comment = models.TextField(null=True, blank=True, verbose_name=_('comment')) altitude_quest = models.BooleanField(_('altitude quest'), default=True) @@ -517,10 +534,11 @@ class RangingBeacon(SpaceGeometryMixin, models.Model): @property def title(self): - if self.node_number is not None or self.wifi_bssids: + if self.node_number is not None or self.wifi_bssids or self.ap_name: if self.comment: - return f'{self.node_number or ''} {''.join(self.wifi_bssids[:1])} ({self.comment})'.strip() + return (f'{self.node_number or ''} {''.join(self.wifi_bssids[:1])} {self.ap_name or ''} ' + f' ({self.comment})').strip() else: - return f'{self.node_number or ''} {''.join(self.wifi_bssids[:1])}'.strip() + return f'{self.node_number or ''} {''.join(self.wifi_bssids[:1])} {self.ap_name or ''}'.strip() else: return self.comment diff --git a/src/c3nav/mapdata/quests/positioning.py b/src/c3nav/mapdata/quests/positioning.py index 1ef49903..c1b1a9c1 100644 --- a/src/c3nav/mapdata/quests/positioning.py +++ b/src/c3nav/mapdata/quests/positioning.py @@ -91,8 +91,8 @@ class RangingBeaconBSSIDsQuest(Quest): def quest_description(self) -> list[str]: return [ _("This quest only works in the app. It works fully automatically."), - _("We are trying to find the BSSIDs broadcast by “%s”.") % self.obj.title, - _("Please stand near “%s” and wait for the submit button to appear.") % self.obj.title, + _("We are trying to find the BSSIDs broadcast by “%s”.") % self.obj.ap_name, + _("Please stand near “%s” and wait for the submit button to appear.") % self.obj.ap_name, _("Do not close this popup until then."), _("This should happen within less than a minute."), ] @@ -103,7 +103,7 @@ class RangingBeaconBSSIDsQuest(Quest): @classmethod def _qs_for_request(cls, request): - return RangingBeacon.qs_for_request(request).filter(import_tag__startswith="noc:", wifi_bssids=[]) + return RangingBeacon.qs_for_request(request).filter(ap_name__isnull=False, wifi_bssids=[]) class BeaconMeasurementQuestForm(ChangeSetModelForm):