Skip to content
This repository has been archived by the owner on Sep 10, 2024. It is now read-only.

Commit

Permalink
Merge pull request #209 from Amsterdam/feature/json-input
Browse files Browse the repository at this point in the history
Feature/json input
  • Loading branch information
kramer65 authored Jul 14, 2022
2 parents 3465eff + 53954e6 commit 9613eac
Show file tree
Hide file tree
Showing 5 changed files with 210 additions and 12 deletions.
28 changes: 24 additions & 4 deletions src/peoplemeasurement/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@
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 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

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. " \
"The geom can only be inserted using the json."


class LatLongWidget(forms.MultiWidget):
"""
Expand Down Expand Up @@ -121,11 +123,29 @@ class ServicelevelAdmin(ImportExportModelAdmin, admin.ModelAdmin):

@admin.register(Area)
class AreaAdmin(LeafletGeoAdminMixin, admin.ModelAdmin):
list_display = ['name', 'sensor', 'geom', 'area']
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', 'json_input'),
'description': f'<h1><b>{JSON_INPUT_HELP_TEXT}</b></h1>',
}),
)


@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'<h1><b>{JSON_INPUT_HELP_TEXT}</b></h1>',
}),
)
108 changes: 108 additions & 0 deletions src/peoplemeasurement/forms.py
Original file line number Diff line number Diff line change
@@ -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__'
35 changes: 35 additions & 0 deletions src/peoplemeasurement/migrations/0031_auto_20220713_1846.py
Original file line number Diff line number Diff line change
@@ -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'),
),
]
35 changes: 35 additions & 0 deletions src/peoplemeasurement/migrations/0032_auto_20220713_2146.py
Original file line number Diff line number Diff line change
@@ -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'),
),
]
16 changes: 8 additions & 8 deletions src/peoplemeasurement/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',)
Expand All @@ -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',)
Expand Down

0 comments on commit 9613eac

Please sign in to comment.