From c60afb0391aa132b34499379ef815fcebdf27506 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Wed, 20 Apr 2022 11:55:03 +0200 Subject: [PATCH] #138 WIP Improvements * adds geom back writing to form field in case of invalid geometry, so the invalid geometry will be shown again * updates tests * fixes bug where race condition of celery workers could lead to duplicates in parcels (needs migration) --- .../tests/compensation/test_workflow.py | 6 +- .../tests/ecoaccount/test_workflow.py | 3 +- ema/tests/test_workflow.py | 3 +- intervention/tests/test_workflow.py | 3 +- konova/forms.py | 22 ++++-- konova/migrations/0010_auto_20220420_1034.py | 17 +++++ konova/migrations/0011_auto_20220420_1101.py | 25 +++++++ konova/models/geometry.py | 3 +- konova/models/parcel.py | 51 +++++++++++++- konova/tests/test_views.py | 26 ++++++- locale/de/LC_MESSAGES/django.mo | Bin 42105 -> 42281 bytes locale/de/LC_MESSAGES/django.po | 66 ++++++++++-------- 12 files changed, 179 insertions(+), 46 deletions(-) create mode 100644 konova/migrations/0010_auto_20220420_1034.py create mode 100644 konova/migrations/0011_auto_20220420_1101.py diff --git a/compensation/tests/compensation/test_workflow.py b/compensation/tests/compensation/test_workflow.py index 570045f2..386c59c9 100644 --- a/compensation/tests/compensation/test_workflow.py +++ b/compensation/tests/compensation/test_workflow.py @@ -50,10 +50,11 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase): test_id = self.create_dummy_string() test_title = self.create_dummy_string() test_geom = self.create_dummy_geometry() + geom_json = self.create_geojson(test_geom) post_data = { "identifier": test_id, "title": test_title, - "geom": test_geom.geojson, + "geom": geom_json, "intervention": self.intervention.id, } pre_creation_intervention_log_count = self.intervention.log.count() @@ -88,10 +89,11 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase): test_id = self.create_dummy_string() test_title = self.create_dummy_string() test_geom = self.create_dummy_geometry() + geom_json = self.create_geojson(test_geom) post_data = { "identifier": test_id, "title": test_title, - "geom": test_geom.geojson, + "geom": geom_json, } pre_creation_intervention_log_count = self.intervention.log.count() diff --git a/compensation/tests/ecoaccount/test_workflow.py b/compensation/tests/ecoaccount/test_workflow.py index bd6894ae..d1a3cf09 100644 --- a/compensation/tests/ecoaccount/test_workflow.py +++ b/compensation/tests/ecoaccount/test_workflow.py @@ -40,12 +40,13 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase): test_id = self.create_dummy_string() test_title = self.create_dummy_string() test_geom = self.create_dummy_geometry() + geom_json = self.create_geojson(test_geom) test_deductable_surface = 1000 test_conservation_office = self.get_conservation_office_code() post_data = { "identifier": test_id, "title": test_title, - "geom": test_geom.geojson, + "geom": geom_json, "deductable_surface": test_deductable_surface, "conservation_office": test_conservation_office.id } diff --git a/ema/tests/test_workflow.py b/ema/tests/test_workflow.py index ecc3f195..4391fdc6 100644 --- a/ema/tests/test_workflow.py +++ b/ema/tests/test_workflow.py @@ -41,11 +41,12 @@ class EmaWorkflowTestCase(BaseWorkflowTestCase): test_id = self.create_dummy_string() test_title = self.create_dummy_string() test_geom = self.create_dummy_geometry() + geom_json = self.create_geojson(test_geom) test_conservation_office = self.get_conservation_office_code() post_data = { "identifier": test_id, "title": test_title, - "geom": test_geom.geojson, + "geom": geom_json, "conservation_office": test_conservation_office.id } self.client_user.post(new_url, post_data) diff --git a/intervention/tests/test_workflow.py b/intervention/tests/test_workflow.py index 66770977..9124c3e8 100644 --- a/intervention/tests/test_workflow.py +++ b/intervention/tests/test_workflow.py @@ -46,6 +46,7 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase): test_id = self.create_dummy_string() test_title = self.create_dummy_string() test_geom = self.create_dummy_geometry() + geom_json = self.create_geojson(test_geom) new_url = reverse("intervention:new", args=()) @@ -59,7 +60,7 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase): post_data = { "identifier": test_id, "title": test_title, - "geom": test_geom.geojson, + "geom": geom_json, } response = self.client_user.post( new_url, diff --git a/konova/forms.py b/konova/forms.py index 0d7f02fe..fec2966a 100644 --- a/konova/forms.py +++ b/konova/forms.py @@ -293,26 +293,33 @@ class SimpleGeomForm(BaseForm): geom = self.instance.geometry.geom geom.transform(ct=DEFAULT_SRID_RLP) self.empty = geom.empty + if self.empty: + raise AttributeError geom = geom.geojson except AttributeError: # If no geometry exists for this form, we simply set the value to None and zoom to the maximum level geom = "" self.empty = True - self.fields["geom"].widget.attrs["default_zoom"] = 1 self.initialize_form_field("geom", geom) def is_valid(self): - super_valid = super().is_valid() + super().is_valid() is_valid = True # Get geojson from form geom = self.data["geom"] geom = json.loads(geom) + # Write submitted data back into form field to make sure invalid geometry + # will be rendered again on failed submit + self.initialize_form_field("geom", self.data["geom"]) + # Read geojson into gdal geometry + # HINT: This can be simplified if the geojson format holds data in epsg:4326 (GDAL provides direct creation for + # this case) features = [] - features_json = geom["features"] + features_json = geom.get("features", []) for feature in features_json: g = gdal.OGRGeometry(json.dumps(feature["geometry"]), srs=DEFAULT_SRID_RLP) if g.geom_type not in ["Polygon", "MultiPolygon"]: @@ -327,9 +334,12 @@ class SimpleGeomForm(BaseForm): form_geom.transform(coord_trans=DEFAULT_SRID) if form_geom.geom_type != "MultiPolygon": form_geom = MultiPolygon(MultiPolygon.from_ewkt(form_geom.ewkt)) - self.cleaned_data = { - "geom": form_geom.wkt - } + + # Write unioned Multipolygon into cleaned data + if self.cleaned_data is None: + self.cleaned_data = {} + self.cleaned_data["geom"] = form_geom.wkt + return is_valid def save(self, action: UserActionLogEntry): diff --git a/konova/migrations/0010_auto_20220420_1034.py b/konova/migrations/0010_auto_20220420_1034.py new file mode 100644 index 00000000..c916ff54 --- /dev/null +++ b/konova/migrations/0010_auto_20220420_1034.py @@ -0,0 +1,17 @@ +# Generated by Django 3.1.3 on 2022-04-20 08:34 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('konova', '0009_auto_20220411_1004'), + ] + + operations = [ + migrations.AddConstraint( + model_name='parcel', + constraint=models.UniqueConstraint(fields=('district', 'municipal', 'parcel_group', 'flr', 'flrstck_nnr', 'flrstck_zhlr'), name='Unique parcel constraint'), + ), + ] diff --git a/konova/migrations/0011_auto_20220420_1101.py b/konova/migrations/0011_auto_20220420_1101.py new file mode 100644 index 00000000..b402818e --- /dev/null +++ b/konova/migrations/0011_auto_20220420_1101.py @@ -0,0 +1,25 @@ +# Generated by Django 3.1.3 on 2022-04-20 09:01 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('konova', '0010_auto_20220420_1034'), + ] + + operations = [ + migrations.AddConstraint( + model_name='district', + constraint=models.UniqueConstraint(fields=('key', 'name'), name='Unique district constraint'), + ), + migrations.AddConstraint( + model_name='municipal', + constraint=models.UniqueConstraint(fields=('key', 'name', 'district'), name='Unique municipal constraint'), + ), + migrations.AddConstraint( + model_name='parcelgroup', + constraint=models.UniqueConstraint(fields=('key', 'name', 'municipal'), name='Unique parcel group constraint'), + ), + ] diff --git a/konova/models/geometry.py b/konova/models/geometry.py index a71a2afa..3460c3dd 100644 --- a/konova/models/geometry.py +++ b/konova/models/geometry.py @@ -6,7 +6,7 @@ Created on: 15.11.21 """ from django.contrib.gis.db.models import MultiPolygonField -from django.db import models +from django.db import models, transaction from django.utils import timezone from konova.models import BaseResource, UuidModel @@ -96,6 +96,7 @@ class Geometry(BaseResource): objs += set_objs return objs + @transaction.atomic def update_parcels(self): """ Updates underlying parcel information diff --git a/konova/models/parcel.py b/konova/models/parcel.py index f74b7af9..cc91e006 100644 --- a/konova/models/parcel.py +++ b/konova/models/parcel.py @@ -39,7 +39,17 @@ class District(UuidModel, AdministrativeSpatialReference): """ The model District refers to "Kreis" """ - pass + + class Meta: + constraints = [ + models.UniqueConstraint( + fields=[ + "key", + "name", + ], + name="Unique district constraint" + ) + ] class Municipal(UuidModel, AdministrativeSpatialReference): @@ -53,6 +63,18 @@ class Municipal(UuidModel, AdministrativeSpatialReference): blank=True, ) + class Meta: + constraints = [ + models.UniqueConstraint( + fields=[ + "key", + "name", + "district", + ], + name="Unique municipal constraint" + ) + ] + class ParcelGroup(UuidModel, AdministrativeSpatialReference): """ The model ParcelGroup refers to "Gemarkung", which is defined as a loose group of parcels @@ -65,6 +87,18 @@ class ParcelGroup(UuidModel, AdministrativeSpatialReference): blank=True, ) + class Meta: + constraints = [ + models.UniqueConstraint( + fields=[ + "key", + "name", + "municipal", + ], + name="Unique parcel group constraint" + ) + ] + class Parcel(UuidModel): """ The Parcel model holds administrative data on covered properties. @@ -106,6 +140,21 @@ class Parcel(UuidModel): ) updated_on = models.DateTimeField(auto_now_add=True) + class Meta: + constraints = [ + models.UniqueConstraint( + fields=[ + "district", + "municipal", + "parcel_group", + "flr", + "flrstck_nnr", + "flrstck_zhlr", + ], + name="Unique parcel constraint" + ) + ] + def __str__(self): return f"{self.parcel_group} | {self.flr} | {self.flrstck_zhlr} | {self.flrstck_nnr}" diff --git a/konova/tests/test_views.py b/konova/tests/test_views.py index 28b3f8a2..dd3c0e14 100644 --- a/konova/tests/test_views.py +++ b/konova/tests/test_views.py @@ -6,9 +6,11 @@ Created on: 26.10.21 """ import datetime +import json from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID from ema.models import Ema +from konova.sub_settings.lanis_settings import DEFAULT_SRID_RLP from user.models import User, Team from django.contrib.auth.models import Group from django.contrib.gis.geos import MultiPolygon, Polygon @@ -287,8 +289,28 @@ class BaseTestCase(TestCase): """ polygon = Polygon.from_bbox((7.592449, 50.359385, 7.593382, 50.359874)) polygon.srid = 4326 - polygon = polygon.transform(3857, clone=True) - return MultiPolygon(polygon, srid=3857) # 3857 is the default srid used for MultiPolygonField in the form + polygon = polygon.transform(DEFAULT_SRID_RLP, clone=True) + return MultiPolygon(polygon, srid=DEFAULT_SRID_RLP) + + def create_geojson(self, geometry): + """ Creates a default structure including geojson from a geometry + + Args: + geometry (): + + Returns: + + """ + geom_json = { + "features": [ + { + "type": "Feature", + "geometry": json.loads(geometry.geojson), + } + ] + } + geom_json = json.dumps(geom_json) + return geom_json def create_dummy_handler(self) -> Handler: """ Creates a Handler diff --git a/locale/de/LC_MESSAGES/django.mo b/locale/de/LC_MESSAGES/django.mo index 9626e763ea04a135d76675baf28ba22d85ea7eea..6eb446ee2c20494019d1a64e878046873b09f6ca 100644 GIT binary patch delta 11907 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 delta 11752 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 Y3D1sgyHm@?Z!6mDcI|ClW_=a?KhUg\n" "Language-Team: LANGUAGE \n" @@ -77,7 +77,7 @@ msgstr "Bericht generieren" msgid "Select a timespan and the desired conservation office" msgstr "Wählen Sie die Zeitspanne und die gewünschte Eintragungsstelle" -#: analysis/forms.py:69 konova/forms.py:225 +#: analysis/forms.py:69 konova/forms.py:227 msgid "Continue" msgstr "Weiter" @@ -342,7 +342,7 @@ msgstr "Automatisch generiert" #: intervention/templates/intervention/detail/includes/documents.html:28 #: intervention/templates/intervention/detail/view.html:31 #: intervention/templates/intervention/report/report.html:12 -#: konova/forms.py:392 +#: konova/forms.py:425 msgid "Title" msgstr "Bezeichnung" @@ -369,7 +369,7 @@ msgstr "Kompensation XY; Flur ABC" #: intervention/templates/intervention/detail/includes/documents.html:34 #: intervention/templates/intervention/detail/includes/payments.html:34 #: intervention/templates/intervention/detail/includes/revocation.html:38 -#: konova/forms.py:427 konova/templates/konova/includes/comment_card.html:16 +#: konova/forms.py:460 konova/templates/konova/includes/comment_card.html:16 msgid "Comment" msgstr "Kommentar" @@ -493,7 +493,7 @@ msgid "Due on which date" msgstr "Zahlung wird an diesem Datum erwartet" #: compensation/forms/modalForms.py:64 compensation/forms/modalForms.py:359 -#: intervention/forms/modalForms.py:177 konova/forms.py:429 +#: intervention/forms/modalForms.py:177 konova/forms.py:462 msgid "Additional comment, maximum {} letters" msgstr "Zusätzlicher Kommentar, maximal {} Zeichen" @@ -538,7 +538,7 @@ msgstr "Neuer Zustand" msgid "Insert data for the new state" msgstr "Geben Sie die Daten des neuen Zustandes ein" -#: compensation/forms/modalForms.py:217 konova/forms.py:227 +#: compensation/forms/modalForms.py:217 konova/forms.py:229 msgid "Object removed" msgstr "Objekt entfernt" @@ -871,7 +871,7 @@ msgstr "Dokumente" #: compensation/templates/compensation/detail/eco_account/includes/documents.html:14 #: ema/templates/ema/detail/includes/documents.html:14 #: intervention/templates/intervention/detail/includes/documents.html:14 -#: konova/forms.py:445 +#: konova/forms.py:478 msgid "Add new document" msgstr "Neues Dokument hinzufügen" @@ -879,7 +879,7 @@ msgstr "Neues Dokument hinzufügen" #: compensation/templates/compensation/detail/eco_account/includes/documents.html:31 #: ema/templates/ema/detail/includes/documents.html:31 #: intervention/templates/intervention/detail/includes/documents.html:31 -#: konova/forms.py:402 +#: konova/forms.py:435 msgid "Created on" msgstr "Erstellt" @@ -887,7 +887,7 @@ msgstr "Erstellt" #: compensation/templates/compensation/detail/eco_account/includes/documents.html:61 #: ema/templates/ema/detail/includes/documents.html:61 #: intervention/templates/intervention/detail/includes/documents.html:65 -#: konova/forms.py:507 +#: konova/forms.py:540 msgid "Edit document" msgstr "Dokument bearbeiten" @@ -1316,7 +1316,7 @@ msgstr "Datum Bestandskraft" msgid "New intervention" msgstr "Neuer Eingriff" -#: intervention/forms/forms.py:294 +#: intervention/forms/forms.py:298 msgid "Edit intervention" msgstr "Eingriff bearbeiten" @@ -1392,7 +1392,7 @@ msgstr "Kompensationen und Zahlungen geprüft" msgid "Run check" msgstr "Prüfung vornehmen" -#: intervention/forms/modalForms.py:264 konova/forms.py:548 +#: intervention/forms/modalForms.py:264 konova/forms.py:581 msgid "" "I, {} {}, confirm that all necessary control steps have been performed by " "myself." @@ -1637,69 +1637,73 @@ msgstr "Nach Zulassungsbehörde suchen" msgid "Search for conservation office" msgstr "Nch Eintragungsstelle suchen" -#: konova/forms.py:39 templates/form/collapsable/form.html:62 +#: konova/forms.py:41 templates/form/collapsable/form.html:62 msgid "Save" msgstr "Speichern" -#: konova/forms.py:73 +#: konova/forms.py:75 msgid "Not editable" msgstr "Nicht editierbar" -#: konova/forms.py:176 konova/forms.py:348 +#: konova/forms.py:178 konova/forms.py:381 msgid "Confirm" msgstr "Bestätige" -#: konova/forms.py:188 konova/forms.py:357 +#: konova/forms.py:190 konova/forms.py:390 msgid "Remove" msgstr "Löschen" -#: konova/forms.py:190 +#: konova/forms.py:192 msgid "You are about to remove {} {}" msgstr "Sie sind dabei {} {} zu löschen" -#: konova/forms.py:277 konova/utils/quality.py:44 konova/utils/quality.py:46 +#: konova/forms.py:280 konova/utils/quality.py:44 konova/utils/quality.py:46 #: templates/form/collapsable/form.html:45 msgid "Geometry" msgstr "Geometrie" -#: konova/forms.py:358 +#: konova/forms.py:325 +msgid "Only surfaces allowed. Points or lines must be buffered." +msgstr "Nur Flächen erlaubt. Punkte oder Linien müssen zu Flächen gepuffert werden." + +#: konova/forms.py:391 msgid "Are you sure?" msgstr "Sind Sie sicher?" -#: konova/forms.py:404 +#: konova/forms.py:437 msgid "When has this file been created? Important for photos." msgstr "Wann wurde diese Datei erstellt oder das Foto aufgenommen?" -#: konova/forms.py:415 +#: konova/forms.py:448 #: venv/lib/python3.7/site-packages/django/db/models/fields/files.py:231 msgid "File" msgstr "Datei" -#: konova/forms.py:417 +#: konova/forms.py:450 msgid "Allowed formats: pdf, jpg, png. Max size 15 MB." msgstr "Formate: pdf, jpg, png. Maximal 15 MB." -#: konova/forms.py:482 +#: konova/forms.py:515 msgid "Added document" msgstr "Dokument hinzugefügt" -#: konova/forms.py:539 +#: konova/forms.py:572 msgid "Confirm record" msgstr "Verzeichnen bestätigen" -#: konova/forms.py:547 +#: konova/forms.py:580 msgid "Record data" msgstr "Daten verzeichnen" -#: konova/forms.py:554 +#: konova/forms.py:587 msgid "Confirm unrecord" msgstr "Entzeichnen bestätigen" -#: konova/forms.py:555 +#: konova/forms.py:588 msgid "Unrecord data" msgstr "Daten entzeichnen" -#: konova/forms.py:556 +#: konova/forms.py:589 msgid "I, {} {}, confirm that this data must be unrecorded." msgstr "" "Ich, {} {}, bestätige, dass diese Daten wieder entzeichnet werden müssen." @@ -2066,7 +2070,7 @@ msgstr "{} wurde erfolgreich vom Nutzer {} geprüft! {}" msgid "missing" msgstr "fehlt" -#: konova/views.py:96 templates/navbars/navbar.html:16 +#: konova/views.py:99 templates/navbars/navbar.html:16 msgid "Home" msgstr "Home"