From c171a21e14db7aa6baa3574c25a05d5f5f6c3e1b Mon Sep 17 00:00:00 2001 From: Hielke Date: Wed, 13 Jul 2022 15:40:08 +0200 Subject: [PATCH 1/4] POC of json input for area. Very WIP. --- src/peoplemeasurement/admin.py | 53 +++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/src/peoplemeasurement/admin.py b/src/peoplemeasurement/admin.py index 2079ab8..6485a24 100644 --- a/src/peoplemeasurement/admin.py +++ b/src/peoplemeasurement/admin.py @@ -1,10 +1,9 @@ +from django.contrib.gis.geos.error import GEOSException from django import forms from django.contrib import admin from django.contrib.gis.db import models as geomodels -from django.contrib.gis.geos import Point -from django.forms.widgets import TextInput +from django.contrib.gis.geos import Point, Polygon from import_export.admin import ImportExportModelAdmin -from import_export.fields import Field from import_export.resources import ModelResource from import_export.tmp_storages import CacheStorage from leaflet.admin import LeafletGeoAdminMixin @@ -119,11 +118,57 @@ class ServicelevelAdmin(ImportExportModelAdmin, admin.ModelAdmin): tmp_storage_class = CacheStorage +class AreaForm(forms.ModelForm): + geom_json_input = forms.JSONField() + + def save(self, commit=True): + geom_json_input = self.cleaned_data.get('geom_json_input', None) + if geom_json_input: + # There is json input, so we overwrite all fields with the info from the json + try: + instance = super(AreaForm, self).save(commit=commit) + sensor_objectnummer = geom_json_input['sensor'] + instance.sensor = Sensors.objects.filter(objectnummer=sensor_objectnummer)[0] + # TODO: catch error if sensor does not exist + instance.name = geom_json_input['areas']['area_id'] + instance.area = geom_json_input['areas']['area'] + geom_points = geom_json_input['areas']['points'] + instance.geom = Polygon([(coordinate['longitude'], coordinate['latitude']) for coordinate in geom_points]) + # TODO: Catch error if points don't form a closed loop (if the last coordinates are not the same as the first one) django.contrib.gis.geos.error.GEOSException + if commit: + instance.save() + except GEOSException as e: + return "a message that it's not a closed loop" + except Exception as e: + # TODO: log something here. Maybe give feedback in the UI? + raise e + + return instance + + class Meta: + model = Area + fields = '__all__' + + @admin.register(Area) class AreaAdmin(LeafletGeoAdminMixin, admin.ModelAdmin): - list_display = ['name', 'sensor', 'geom', 'area'] + form = AreaForm + fieldsets = ( + (None, { + 'fields': ('name', 'sensor', 'area', 'geom', 'geom_json_input'), + }), + ) + + list_display = ['name', 'sensor', 'area'] tmp_storage_class = CacheStorage + # TODO: + # - Maak json input niet required + # - Haal "null" weg uit geom json input field + # - Show leaflet ding zonder er dingen in op te kunnen slaan + # - Give instructions in UI that json overwrites data in input fields + + @admin.register(Line) class LineAdmin(LeafletGeoAdminMixin, admin.ModelAdmin): From 2ea9df2c43c85c732cc95d3525bf090506d8dde4 Mon Sep 17 00:00:00 2001 From: Hielke Date: Wed, 13 Jul 2022 18:47:40 +0200 Subject: [PATCH 2/4] Added simple validation errors in the area form --- src/peoplemeasurement/admin.py | 78 +++++++++++++------ .../migrations/0031_auto_20220713_1846.py | 35 +++++++++ src/peoplemeasurement/models.py | 8 +- 3 files changed, 92 insertions(+), 29 deletions(-) create mode 100644 src/peoplemeasurement/migrations/0031_auto_20220713_1846.py diff --git a/src/peoplemeasurement/admin.py b/src/peoplemeasurement/admin.py index 6485a24..918988d 100644 --- a/src/peoplemeasurement/admin.py +++ b/src/peoplemeasurement/admin.py @@ -10,6 +10,9 @@ from peoplemeasurement.models import Area, Line, Sensors, Servicelevel +JSON_INPUT_HELP_TEXT = "Adding json overwrites all manually input fields. " \ + "The geom can only be inserted using the json." + class LatLongWidget(forms.MultiWidget): """ @@ -119,31 +122,50 @@ class ServicelevelAdmin(ImportExportModelAdmin, admin.ModelAdmin): class AreaForm(forms.ModelForm): - geom_json_input = forms.JSONField() + geom_json_input = forms.JSONField(required=False) + + def clean(self): + cleaned_data = super(AreaForm, self).clean() + + # TODO: Use a serializer to do this properly + # TODO: Also raise a ValidationError if it's a new object and no json is inserted (this is not possible, since the area points can only be inserted using the json). + geom_json_input = cleaned_data.get('geom_json_input') + if geom_json_input: + if not geom_json_input.get('sensor'): + raise forms.ValidationError("Sensor missing in json") + if Sensors.objects.filter(objectnummer=geom_json_input['sensor']).count() == 0: + raise forms.ValidationError(f"Sensor with objectnummer '{geom_json_input['sensor']}' does not exist.") + if not geom_json_input.get('areas'): + raise forms.ValidationError("Sensor missing in json") + if not geom_json_input['areas'].get('area_id'): + raise forms.ValidationError("area_id") + if not geom_json_input['areas'].get('area'): + raise forms.ValidationError("area") + if not geom_json_input['areas'].get('points'): + raise forms.ValidationError("points") + points = geom_json_input['areas']['points'] + if len(points) <= 3: # The last point should be the same as the first point, and we need at least a triangle to have an area + raise forms.ValidationError("Not enough points") + if points[0] != points[-1]: + raise forms.ValidationError("The geom points in the json do not form a closed loop.") + + return cleaned_data def save(self, commit=True): + instance = super(AreaForm, self).save(commit=commit) geom_json_input = self.cleaned_data.get('geom_json_input', None) - if geom_json_input: + if geom_json_input and geom_json_input != 'null': # There is json input, so we overwrite all fields with the info from the json - try: - instance = super(AreaForm, self).save(commit=commit) - sensor_objectnummer = geom_json_input['sensor'] - instance.sensor = Sensors.objects.filter(objectnummer=sensor_objectnummer)[0] - # TODO: catch error if sensor does not exist - instance.name = geom_json_input['areas']['area_id'] - instance.area = geom_json_input['areas']['area'] - geom_points = geom_json_input['areas']['points'] - instance.geom = Polygon([(coordinate['longitude'], coordinate['latitude']) for coordinate in geom_points]) - # TODO: Catch error if points don't form a closed loop (if the last coordinates are not the same as the first one) django.contrib.gis.geos.error.GEOSException - if commit: - instance.save() - except GEOSException as e: - return "a message that it's not a closed loop" - except Exception as e: - # TODO: log something here. Maybe give feedback in the UI? - raise e - - return instance + sensor_objectnummer = geom_json_input['sensor'] + instance.sensor = Sensors.objects.filter(objectnummer=sensor_objectnummer)[0] + instance.name = geom_json_input['areas']['area_id'] + instance.area = geom_json_input['areas']['area'] + geom_points = geom_json_input['areas']['points'] + instance.geom = Polygon([(coordinate['longitude'], coordinate['latitude']) for coordinate in geom_points]) + if commit: + instance.save() + + return instance class Meta: model = Area @@ -152,10 +174,12 @@ class Meta: @admin.register(Area) class AreaAdmin(LeafletGeoAdminMixin, admin.ModelAdmin): + modifiable = False # Make the leaflet map read-only form = AreaForm fieldsets = ( (None, { 'fields': ('name', 'sensor', 'area', 'geom', 'geom_json_input'), + 'description': f'

{JSON_INPUT_HELP_TEXT}

', }), ) @@ -163,10 +187,14 @@ class AreaAdmin(LeafletGeoAdminMixin, admin.ModelAdmin): tmp_storage_class = CacheStorage # TODO: - # - Maak json input niet required - # - Haal "null" weg uit geom json input field - # - Show leaflet ding zonder er dingen in op te kunnen slaan - # - Give instructions in UI that json overwrites data in input fields + # - Test + # - add info using json + # - edit other details in the text input fields + # - add no json and no input => error + # - add malformed json + # - add points which do not form a closed loop + + diff --git a/src/peoplemeasurement/migrations/0031_auto_20220713_1846.py b/src/peoplemeasurement/migrations/0031_auto_20220713_1846.py new file mode 100644 index 0000000..d0418ef --- /dev/null +++ b/src/peoplemeasurement/migrations/0031_auto_20220713_1846.py @@ -0,0 +1,35 @@ +# Generated by Django 3.2.12 on 2022-07-13 16:46 + +import django.contrib.gis.db.models.fields +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('peoplemeasurement', '0030_auto_20220616_1228'), + ] + + operations = [ + migrations.AlterField( + model_name='area', + name='area', + field=models.IntegerField(blank=True), + ), + migrations.AlterField( + model_name='area', + name='geom', + field=django.contrib.gis.db.models.fields.PolygonField(blank=True, srid=4326), + ), + migrations.AlterField( + model_name='area', + name='name', + field=models.CharField(blank=True, max_length=255, unique=True), + ), + migrations.AlterField( + model_name='area', + name='sensor', + field=models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, related_name='areas', to='peoplemeasurement.sensors'), + ), + ] diff --git a/src/peoplemeasurement/models.py b/src/peoplemeasurement/models.py index 72fa53f..8cc8719 100644 --- a/src/peoplemeasurement/models.py +++ b/src/peoplemeasurement/models.py @@ -54,10 +54,10 @@ class Servicelevel(models.Model): class Area(models.Model): - sensor = models.ForeignKey('Sensors', related_name='areas', on_delete=models.CASCADE) - name = models.CharField(max_length=255, unique=True) # Naam van meetgebied - geom = models.PolygonField() # Polygoon dat het meetgebied omvat - area = models.IntegerField() # Oppervlakte van het meetgebied in m2 + sensor = models.ForeignKey('Sensors', related_name='areas', on_delete=models.CASCADE, blank=True) + name = models.CharField(max_length=255, unique=True, blank=True) # Naam van meetgebied + geom = models.PolygonField(blank=True) # Polygoon dat het meetgebied omvat + area = models.IntegerField(blank=True) # Oppervlakte van het meetgebied in m2 class Meta: unique_together = ('sensor', 'name',) From 45fa458dab03d8385d44760188f18b4e0ec83c6b Mon Sep 17 00:00:00 2001 From: Hielke Date: Wed, 13 Jul 2022 21:52:52 +0200 Subject: [PATCH 3/4] Added LineForm and some more improvements --- src/peoplemeasurement/admin.py | 125 ++++++++++++------ .../migrations/0032_auto_20220713_2146.py | 35 +++++ src/peoplemeasurement/models.py | 8 +- 3 files changed, 127 insertions(+), 41 deletions(-) create mode 100644 src/peoplemeasurement/migrations/0032_auto_20220713_2146.py diff --git a/src/peoplemeasurement/admin.py b/src/peoplemeasurement/admin.py index 918988d..1ffcdf2 100644 --- a/src/peoplemeasurement/admin.py +++ b/src/peoplemeasurement/admin.py @@ -1,8 +1,7 @@ -from django.contrib.gis.geos.error import GEOSException from django import forms from django.contrib import admin from django.contrib.gis.db import models as geomodels -from django.contrib.gis.geos import Point, Polygon +from django.contrib.gis.geos import LineString, Point, Polygon from import_export.admin import ImportExportModelAdmin from import_export.resources import ModelResource from import_export.tmp_storages import CacheStorage @@ -121,29 +120,32 @@ class ServicelevelAdmin(ImportExportModelAdmin, admin.ModelAdmin): tmp_storage_class = CacheStorage +# TODO: Move the forms to a separate forms.py class AreaForm(forms.ModelForm): - geom_json_input = forms.JSONField(required=False) + json_input = forms.JSONField(required=False) def clean(self): - cleaned_data = super(AreaForm, self).clean() + cleaned_data = super().clean() # TODO: Use a serializer to do this properly # TODO: Also raise a ValidationError if it's a new object and no json is inserted (this is not possible, since the area points can only be inserted using the json). - geom_json_input = cleaned_data.get('geom_json_input') - if geom_json_input: - if not geom_json_input.get('sensor'): - raise forms.ValidationError("Sensor missing in json") - if Sensors.objects.filter(objectnummer=geom_json_input['sensor']).count() == 0: - raise forms.ValidationError(f"Sensor with objectnummer '{geom_json_input['sensor']}' does not exist.") - if not geom_json_input.get('areas'): - raise forms.ValidationError("Sensor missing in json") - if not geom_json_input['areas'].get('area_id'): - raise forms.ValidationError("area_id") - if not geom_json_input['areas'].get('area'): - raise forms.ValidationError("area") - if not geom_json_input['areas'].get('points'): - raise forms.ValidationError("points") - points = geom_json_input['areas']['points'] + json_input = cleaned_data.get('json_input') + if json_input: + # Check keys + for k in ('sensor', 'areas'): + if not json_input.get(k): + raise forms.ValidationError(f"{k} missing in json") + for k in ('area_id', 'area', 'points'): + if not json_input['areas'].get('area_id'): + raise forms.ValidationError(f"{k} missing in json") + + # Check if sensor exists + # TODO: move this to the model + if Sensors.objects.filter(objectnummer=json_input['sensor']).count() == 0: + raise forms.ValidationError(f"Sensor with objectnummer '{json_input['sensor']}' does not exist.") + + # Check points + points = json_input['areas']['points'] if len(points) <= 3: # The last point should be the same as the first point, and we need at least a triangle to have an area raise forms.ValidationError("Not enough points") if points[0] != points[-1]: @@ -152,15 +154,15 @@ def clean(self): return cleaned_data def save(self, commit=True): - instance = super(AreaForm, self).save(commit=commit) - geom_json_input = self.cleaned_data.get('geom_json_input', None) - if geom_json_input and geom_json_input != 'null': + instance = super().save(commit=commit) + json_input = self.cleaned_data.get('json_input', None) + if json_input and json_input != 'null': # There is json input, so we overwrite all fields with the info from the json - sensor_objectnummer = geom_json_input['sensor'] + sensor_objectnummer = json_input['sensor'] instance.sensor = Sensors.objects.filter(objectnummer=sensor_objectnummer)[0] - instance.name = geom_json_input['areas']['area_id'] - instance.area = geom_json_input['areas']['area'] - geom_points = geom_json_input['areas']['points'] + instance.name = json_input['areas']['area_id'] + instance.area = json_input['areas']['area'] + geom_points = json_input['areas']['points'] instance.geom = Polygon([(coordinate['longitude'], coordinate['latitude']) for coordinate in geom_points]) if commit: instance.save() @@ -174,31 +176,80 @@ class Meta: @admin.register(Area) class AreaAdmin(LeafletGeoAdminMixin, admin.ModelAdmin): + list_display = ['name', 'sensor', 'area', 'geom'] + tmp_storage_class = CacheStorage + modifiable = False # Make the leaflet map read-only form = AreaForm fieldsets = ( (None, { - 'fields': ('name', 'sensor', 'area', 'geom', 'geom_json_input'), + 'fields': ('name', 'sensor', 'area', 'geom', 'json_input'), 'description': f'

{JSON_INPUT_HELP_TEXT}

', }), ) - list_display = ['name', 'sensor', 'area'] - tmp_storage_class = CacheStorage - # TODO: - # - Test - # - add info using json - # - edit other details in the text input fields - # - add no json and no input => error - # - add malformed json - # - add points which do not form a closed loop +class LineForm(forms.ModelForm): + json_input = forms.JSONField(required=False) + def clean(self): + cleaned_data = super().clean() + + # TODO: Use a serializer to do this properly + # TODO: Also raise a ValidationError if it's a new object and no json is inserted (this is not possible, since the area points can only be inserted using the json). + json_input = cleaned_data.get('json_input') + if json_input: + # Check keys + for k in ('sensor', 'lines'): + if not json_input.get(k): + raise forms.ValidationError(f"{k} missing in json") + for k in ('line_id', 'azimuth', 'points'): + if not json_input['lines'].get(k): + raise forms.ValidationError(f"{k} missing in json") + + # Check if sensor exists + # TODO: move this to the model + if Sensors.objects.filter(objectnummer=json_input['sensor']).count() == 0: + raise forms.ValidationError(f"Sensor with objectnummer '{json_input['sensor']}' does not exist.") + + # Check points + points = json_input['lines']['points'] + if len(points) < 2: + raise forms.ValidationError("We need at least two points") + + return cleaned_data + + def save(self, commit=True): + instance = super().save(commit=commit) + json_input = self.cleaned_data.get('json_input', None) + if json_input: + # There is json input, so we overwrite all fields with the info from the json + sensor_objectnummer = json_input['sensor'] + instance.sensor = Sensors.objects.filter(objectnummer=sensor_objectnummer)[0] + instance.name = json_input['lines']['line_id'] + instance.azimuth = json_input['lines']['azimuth'] + geom_points = json_input['lines']['points'] + instance.geom = LineString([(coordinate['longitude'], coordinate['latitude']) for coordinate in geom_points]) + if commit: + instance.save() + return instance + class Meta: + model = Area + fields = '__all__' @admin.register(Line) class LineAdmin(LeafletGeoAdminMixin, admin.ModelAdmin): - list_display = ['name', 'sensor', 'geom', 'azimuth'] + list_display = ['name', 'sensor', 'azimuth', 'geom'] tmp_storage_class = CacheStorage + + modifiable = False # Make the leaflet map read-only + form = LineForm + fieldsets = ( + (None, { + 'fields': ('name', 'sensor', 'azimuth', 'geom', 'json_input'), + 'description': f'

{JSON_INPUT_HELP_TEXT}

', + }), + ) diff --git a/src/peoplemeasurement/migrations/0032_auto_20220713_2146.py b/src/peoplemeasurement/migrations/0032_auto_20220713_2146.py new file mode 100644 index 0000000..741dece --- /dev/null +++ b/src/peoplemeasurement/migrations/0032_auto_20220713_2146.py @@ -0,0 +1,35 @@ +# Generated by Django 3.2.12 on 2022-07-13 19:46 + +import django.contrib.gis.db.models.fields +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('peoplemeasurement', '0031_auto_20220713_1846'), + ] + + operations = [ + migrations.AlterField( + model_name='line', + name='azimuth', + field=models.FloatField(blank=True), + ), + migrations.AlterField( + model_name='line', + name='geom', + field=django.contrib.gis.db.models.fields.LineStringField(blank=True, srid=4326), + ), + migrations.AlterField( + model_name='line', + name='name', + field=models.CharField(blank=True, max_length=255, unique=True), + ), + migrations.AlterField( + model_name='line', + name='sensor', + field=models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, related_name='lines', to='peoplemeasurement.sensors'), + ), + ] diff --git a/src/peoplemeasurement/models.py b/src/peoplemeasurement/models.py index 8cc8719..2b155e4 100644 --- a/src/peoplemeasurement/models.py +++ b/src/peoplemeasurement/models.py @@ -67,10 +67,10 @@ def __str__(self): class Line(models.Model): - sensor = models.ForeignKey('Sensors', related_name='lines', on_delete=models.CASCADE) - name = models.CharField(max_length=255, unique=True) # Naam van de tellijn - geom = models.LineStringField() # Lijn die de tellijn definieert - azimuth = models.FloatField() # Azimuth van de looprichting van de passage + sensor = models.ForeignKey('Sensors', related_name='lines', on_delete=models.CASCADE, blank=True) + name = models.CharField(max_length=255, unique=True, blank=True) # Naam van de tellijn + geom = models.LineStringField(blank=True) # Lijn die de tellijn definieert + azimuth = models.FloatField(blank=True) # Azimuth van de looprichting van de passage class Meta: unique_together = ('sensor', 'name',) From 53954e66c46cf54329b2761f000a3b807844dece Mon Sep 17 00:00:00 2001 From: Hielke Date: Thu, 14 Jul 2022 09:24:05 +0200 Subject: [PATCH 4/4] Moved forms into forms.py --- src/peoplemeasurement/admin.py | 108 +-------------------------------- src/peoplemeasurement/forms.py | 108 +++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 106 deletions(-) create mode 100644 src/peoplemeasurement/forms.py diff --git a/src/peoplemeasurement/admin.py b/src/peoplemeasurement/admin.py index 1ffcdf2..6609f93 100644 --- a/src/peoplemeasurement/admin.py +++ b/src/peoplemeasurement/admin.py @@ -1,12 +1,13 @@ from django import forms from django.contrib import admin from django.contrib.gis.db import models as geomodels -from django.contrib.gis.geos import LineString, Point, Polygon +from django.contrib.gis.geos import Point from import_export.admin import ImportExportModelAdmin from import_export.resources import ModelResource from import_export.tmp_storages import CacheStorage from leaflet.admin import LeafletGeoAdminMixin +from peoplemeasurement.forms import AreaForm, LineForm from peoplemeasurement.models import Area, Line, Sensors, Servicelevel JSON_INPUT_HELP_TEXT = "Adding json overwrites all manually input fields. " \ @@ -120,60 +121,6 @@ class ServicelevelAdmin(ImportExportModelAdmin, admin.ModelAdmin): tmp_storage_class = CacheStorage -# TODO: Move the forms to a separate forms.py -class AreaForm(forms.ModelForm): - json_input = forms.JSONField(required=False) - - def clean(self): - cleaned_data = super().clean() - - # TODO: Use a serializer to do this properly - # TODO: Also raise a ValidationError if it's a new object and no json is inserted (this is not possible, since the area points can only be inserted using the json). - json_input = cleaned_data.get('json_input') - if json_input: - # Check keys - for k in ('sensor', 'areas'): - if not json_input.get(k): - raise forms.ValidationError(f"{k} missing in json") - for k in ('area_id', 'area', 'points'): - if not json_input['areas'].get('area_id'): - raise forms.ValidationError(f"{k} missing in json") - - # Check if sensor exists - # TODO: move this to the model - if Sensors.objects.filter(objectnummer=json_input['sensor']).count() == 0: - raise forms.ValidationError(f"Sensor with objectnummer '{json_input['sensor']}' does not exist.") - - # Check points - points = json_input['areas']['points'] - if len(points) <= 3: # The last point should be the same as the first point, and we need at least a triangle to have an area - raise forms.ValidationError("Not enough points") - if points[0] != points[-1]: - raise forms.ValidationError("The geom points in the json do not form a closed loop.") - - return cleaned_data - - def save(self, commit=True): - instance = super().save(commit=commit) - json_input = self.cleaned_data.get('json_input', None) - if json_input and json_input != 'null': - # There is json input, so we overwrite all fields with the info from the json - sensor_objectnummer = json_input['sensor'] - instance.sensor = Sensors.objects.filter(objectnummer=sensor_objectnummer)[0] - instance.name = json_input['areas']['area_id'] - instance.area = json_input['areas']['area'] - geom_points = json_input['areas']['points'] - instance.geom = Polygon([(coordinate['longitude'], coordinate['latitude']) for coordinate in geom_points]) - if commit: - instance.save() - - return instance - - class Meta: - model = Area - fields = '__all__' - - @admin.register(Area) class AreaAdmin(LeafletGeoAdminMixin, admin.ModelAdmin): list_display = ['name', 'sensor', 'area', 'geom'] @@ -189,57 +136,6 @@ class AreaAdmin(LeafletGeoAdminMixin, admin.ModelAdmin): ) -class LineForm(forms.ModelForm): - json_input = forms.JSONField(required=False) - - def clean(self): - cleaned_data = super().clean() - - # TODO: Use a serializer to do this properly - # TODO: Also raise a ValidationError if it's a new object and no json is inserted (this is not possible, since the area points can only be inserted using the json). - json_input = cleaned_data.get('json_input') - if json_input: - # Check keys - for k in ('sensor', 'lines'): - if not json_input.get(k): - raise forms.ValidationError(f"{k} missing in json") - for k in ('line_id', 'azimuth', 'points'): - if not json_input['lines'].get(k): - raise forms.ValidationError(f"{k} missing in json") - - # Check if sensor exists - # TODO: move this to the model - if Sensors.objects.filter(objectnummer=json_input['sensor']).count() == 0: - raise forms.ValidationError(f"Sensor with objectnummer '{json_input['sensor']}' does not exist.") - - # Check points - points = json_input['lines']['points'] - if len(points) < 2: - raise forms.ValidationError("We need at least two points") - - return cleaned_data - - def save(self, commit=True): - instance = super().save(commit=commit) - json_input = self.cleaned_data.get('json_input', None) - if json_input: - # There is json input, so we overwrite all fields with the info from the json - sensor_objectnummer = json_input['sensor'] - instance.sensor = Sensors.objects.filter(objectnummer=sensor_objectnummer)[0] - instance.name = json_input['lines']['line_id'] - instance.azimuth = json_input['lines']['azimuth'] - geom_points = json_input['lines']['points'] - instance.geom = LineString([(coordinate['longitude'], coordinate['latitude']) for coordinate in geom_points]) - if commit: - instance.save() - - return instance - - class Meta: - model = Area - fields = '__all__' - - @admin.register(Line) class LineAdmin(LeafletGeoAdminMixin, admin.ModelAdmin): list_display = ['name', 'sensor', 'azimuth', 'geom'] diff --git a/src/peoplemeasurement/forms.py b/src/peoplemeasurement/forms.py new file mode 100644 index 0000000..3405ea7 --- /dev/null +++ b/src/peoplemeasurement/forms.py @@ -0,0 +1,108 @@ +from django import forms +from django.contrib.gis.geos import LineString, Polygon + +from peoplemeasurement.models import Area, Sensors + + +class AreaForm(forms.ModelForm): + json_input = forms.JSONField(required=False) + + def clean(self): + cleaned_data = super().clean() + + # TODO: Use a serializer to do this properly + # TODO: Also raise a ValidationError if it's a new object and no json is inserted (this is not possible, since the area points can only be inserted using the json). + json_input = cleaned_data.get('json_input') + if json_input: + # Check keys + for k in ('sensor', 'areas'): + if not json_input.get(k): + raise forms.ValidationError(f"{k} missing in json") + for k in ('area_id', 'area', 'points'): + if not json_input['areas'].get('area_id'): + raise forms.ValidationError(f"{k} missing in json") + + # Check if sensor exists + # TODO: move this to the model + if Sensors.objects.filter(objectnummer=json_input['sensor']).count() == 0: + raise forms.ValidationError(f"Sensor with objectnummer '{json_input['sensor']}' does not exist.") + + # Check points + points = json_input['areas']['points'] + if len(points) <= 3: # The last point should be the same as the first point, and we need at least a triangle to have an area + raise forms.ValidationError("Not enough points") + if points[0] != points[-1]: + raise forms.ValidationError("The geom points in the json do not form a closed loop.") + + return cleaned_data + + def save(self, commit=True): + instance = super().save(commit=commit) + json_input = self.cleaned_data.get('json_input', None) + if json_input and json_input != 'null': + # There is json input, so we overwrite all fields with the info from the json + sensor_objectnummer = json_input['sensor'] + instance.sensor = Sensors.objects.filter(objectnummer=sensor_objectnummer)[0] + instance.name = json_input['areas']['area_id'] + instance.area = json_input['areas']['area'] + geom_points = json_input['areas']['points'] + instance.geom = Polygon([(coordinate['longitude'], coordinate['latitude']) for coordinate in geom_points]) + if commit: + instance.save() + + return instance + + class Meta: + model = Area + fields = '__all__' + + +class LineForm(forms.ModelForm): + json_input = forms.JSONField(required=False) + + def clean(self): + cleaned_data = super().clean() + + # TODO: Use a serializer to do this properly + # TODO: Also raise a ValidationError if it's a new object and no json is inserted (this is not possible, since the area points can only be inserted using the json). + json_input = cleaned_data.get('json_input') + if json_input: + # Check keys + for k in ('sensor', 'lines'): + if not json_input.get(k): + raise forms.ValidationError(f"{k} missing in json") + for k in ('line_id', 'azimuth', 'points'): + if not json_input['lines'].get(k): + raise forms.ValidationError(f"{k} missing in json") + + # Check if sensor exists + # TODO: move this to the model + if Sensors.objects.filter(objectnummer=json_input['sensor']).count() == 0: + raise forms.ValidationError(f"Sensor with objectnummer '{json_input['sensor']}' does not exist.") + + # Check points + points = json_input['lines']['points'] + if len(points) < 2: + raise forms.ValidationError("We need at least two points") + + return cleaned_data + + def save(self, commit=True): + instance = super().save(commit=commit) + json_input = self.cleaned_data.get('json_input', None) + if json_input: + # There is json input, so we overwrite all fields with the info from the json + sensor_objectnummer = json_input['sensor'] + instance.sensor = Sensors.objects.filter(objectnummer=sensor_objectnummer)[0] + instance.name = json_input['lines']['line_id'] + instance.azimuth = json_input['lines']['azimuth'] + geom_points = json_input['lines']['points'] + instance.geom = LineString([(coordinate['longitude'], coordinate['latitude']) for coordinate in geom_points]) + if commit: + instance.save() + + return instance + + class Meta: + model = Area + fields = '__all__'