From a6a5bd54505c6dd6580bb87b862b52f42b81281e Mon Sep 17 00:00:00 2001
From: mpeltriaux <michel.peltriaux@sgdnord.rlp.de>
Date: Tue, 22 Nov 2022 15:38:03 +0100
Subject: [PATCH] Further fixes

* fixes race condition on geometry conflict calculation if performed in background process
* simplifies access to smaller buffered geometry
* adds mapping of "qm"->"m2" for UnitChoice in API usage for backwards compatibility
---
 api/utils/serializer/v1/compensation.py |  3 ++-
 api/utils/serializer/v1/ecoaccount.py   |  3 ++-
 api/utils/serializer/v1/ema.py          |  3 ++-
 api/utils/serializer/v1/intervention.py |  3 ++-
 api/utils/serializer/v1/serializer.py   |  3 ++-
 konova/forms/geometry_form.py           |  5 +++--
 konova/models/geometry.py               | 22 +++++++++++++++-------
 konova/utils/wfs/spatial.py             |  2 +-
 8 files changed, 29 insertions(+), 15 deletions(-)

diff --git a/api/utils/serializer/v1/compensation.py b/api/utils/serializer/v1/compensation.py
index 6bddadbf..fbdbba62 100644
--- a/api/utils/serializer/v1/compensation.py
+++ b/api/utils/serializer/v1/compensation.py
@@ -11,7 +11,7 @@ from api.utils.serializer.v1.serializer import AbstractModelAPISerializerV1, Abs
 from compensation.models import Compensation
 from intervention.models import Intervention
 from konova.models import Geometry
-from konova.tasks import celery_update_parcels
+from konova.tasks import celery_update_parcels, celery_check_for_geometry_conflicts
 from konova.utils.message_templates import DATA_UNSHARED
 from user.models import UserActionLogEntry
 
@@ -128,6 +128,7 @@ class CompensationAPISerializerV1(AbstractModelAPISerializerV1, AbstractCompensa
         obj.log.add(obj.created)
 
         celery_update_parcels.delay(obj.geometry.id)
+        celery_check_for_geometry_conflicts.delay(obj.geometry.id)
 
         return obj.id
 
diff --git a/api/utils/serializer/v1/ecoaccount.py b/api/utils/serializer/v1/ecoaccount.py
index d466c34f..c3d27867 100644
--- a/api/utils/serializer/v1/ecoaccount.py
+++ b/api/utils/serializer/v1/ecoaccount.py
@@ -13,7 +13,7 @@ from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID, CODELIST_HANDLER_
 from compensation.models import EcoAccount
 from intervention.models import Legal, Responsibility, Handler
 from konova.models import Geometry
-from konova.tasks import celery_update_parcels
+from konova.tasks import celery_update_parcels, celery_check_for_geometry_conflicts
 from user.models import UserActionLogEntry
 
 
@@ -150,6 +150,7 @@ class EcoAccountAPISerializerV1(AbstractModelAPISerializerV1,
         obj.users.add(user)
 
         celery_update_parcels.delay(obj.geometry.id)
+        celery_check_for_geometry_conflicts.delay(obj.geometry.id)
 
         return obj.id
 
diff --git a/api/utils/serializer/v1/ema.py b/api/utils/serializer/v1/ema.py
index 2f5c596e..4bbb6d9e 100644
--- a/api/utils/serializer/v1/ema.py
+++ b/api/utils/serializer/v1/ema.py
@@ -13,7 +13,7 @@ from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID, CODELIST_HANDLER_
 from ema.models import Ema
 from intervention.models import Responsibility, Handler
 from konova.models import Geometry
-from konova.tasks import celery_update_parcels
+from konova.tasks import celery_update_parcels, celery_check_for_geometry_conflicts
 from user.models import UserActionLogEntry
 
 
@@ -122,6 +122,7 @@ class EmaAPISerializerV1(AbstractModelAPISerializerV1, AbstractCompensationAPISe
         obj.users.add(user)
 
         celery_update_parcels.delay(obj.geometry.id)
+        celery_check_for_geometry_conflicts.delay(obj.geometry.id)
 
         return obj.id
 
diff --git a/api/utils/serializer/v1/intervention.py b/api/utils/serializer/v1/intervention.py
index a6d5084c..dca53d72 100644
--- a/api/utils/serializer/v1/intervention.py
+++ b/api/utils/serializer/v1/intervention.py
@@ -13,7 +13,7 @@ from api.utils.serializer.v1.serializer import AbstractModelAPISerializerV1, \
 from compensation.models import Payment
 from intervention.models import Intervention, Responsibility, Legal, Handler
 from konova.models import Geometry
-from konova.tasks import celery_update_parcels
+from konova.tasks import celery_update_parcels, celery_check_for_geometry_conflicts
 from user.models import UserActionLogEntry
 
 
@@ -165,6 +165,7 @@ class InterventionAPISerializerV1(AbstractModelAPISerializerV1,
         obj.log.add(obj.created)
 
         celery_update_parcels.delay(obj.geometry.id)
+        celery_check_for_geometry_conflicts.delay(obj.geometry.id)
 
         return obj.id
 
diff --git a/api/utils/serializer/v1/serializer.py b/api/utils/serializer/v1/serializer.py
index fe979775..78f8d1f6 100644
--- a/api/utils/serializer/v1/serializer.py
+++ b/api/utils/serializer/v1/serializer.py
@@ -392,7 +392,8 @@ class AbstractCompensationAPISerializerV1Mixin:
                 self._konova_code_from_json(e, CODELIST_COMPENSATION_ACTION_DETAIL_ID) for e in entry["action_details"]
             ]
             amount = float(entry["amount"])
-            unit = entry["unit"]
+            # Mapping of old "qm" into "m²"
+            unit = UnitChoices.m2.value if entry["unit"] == "qm" else entry["unit"]
             comment = entry["comment"]
 
             # Check on validity
diff --git a/konova/forms/geometry_form.py b/konova/forms/geometry_form.py
index b9658b47..95e0a7db 100644
--- a/konova/forms/geometry_form.py
+++ b/konova/forms/geometry_form.py
@@ -15,7 +15,7 @@ from django.utils.translation import gettext_lazy as _
 
 from konova.forms.base_form import BaseForm
 from konova.models import Geometry
-from konova.tasks import celery_update_parcels
+from konova.tasks import celery_update_parcels, celery_check_for_geometry_conflicts
 from konova.sub_settings.lanis_settings import DEFAULT_SRID_RLP
 from user.models import UserActionLogEntry
 
@@ -146,8 +146,9 @@ class SimpleGeomForm(BaseForm):
                 geom=self.cleaned_data.get("geom", MultiPolygon(srid=DEFAULT_SRID_RLP)),
                 created=action,
             )
-        # Start the parcel update procedure in a background process
+        # Start parcel update and geometry conflict checking procedure in a background process
         celery_update_parcels.delay(geometry.id)
+        celery_check_for_geometry_conflicts.delay(geometry.id)
         return geometry
 
     def __flatten_geom_to_2D(self, geom):
diff --git a/konova/models/geometry.py b/konova/models/geometry.py
index f8c272f9..492ba439 100644
--- a/konova/models/geometry.py
+++ b/konova/models/geometry.py
@@ -8,13 +8,11 @@ Created on: 15.11.21
 import json
 
 from django.contrib.gis.db.models import MultiPolygonField
-from django.contrib.gis.geos import Polygon
 from django.db import models, transaction
 from django.utils import timezone
 
 from konova.models import BaseResource, UuidModel
 from konova.sub_settings.lanis_settings import DEFAULT_SRID_RLP
-from konova.tasks import celery_check_for_geometry_conflicts
 from konova.utils.wfs.spatial import ParcelWFSFetcher
 
 
@@ -29,7 +27,18 @@ class Geometry(BaseResource):
 
     def save(self, *args, **kwargs):
         super().save(*args, **kwargs)
-        celery_check_for_geometry_conflicts.delay(self.id)
+
+    @property
+    def geom_small_buffered(self):
+        """
+        Returns a smaller buffered version of the geometry.
+        Can be used to shrink the geometry used for intersection purposes to avoid intersection detection on
+        neighbouring geometries.
+
+        Returns:
+
+        """
+        return self.geom.buffer(-0.001)
 
     def check_for_conflicts(self):
         """ Checks for new geometry overlaps
@@ -44,9 +53,8 @@ class Geometry(BaseResource):
             return None
 
         self.recheck_existing_conflicts()
-
         overlapping_geoms = Geometry.objects.filter(
-            geom__intersects=self.geom,
+            geom__intersects=self.geom_small_buffered,
         ).exclude(
             id=self.id
         ).distinct()
@@ -68,14 +76,14 @@ class Geometry(BaseResource):
         """
         all_conflicts_as_conflicting = self.conflicts_geometries.all()
         still_conflicting_conflicts = all_conflicts_as_conflicting.filter(
-            affected_geometry__geom__intersects=self.geom
+            affected_geometry__geom__intersects=self.geom_small_buffered
         )
         resolved_conflicts = all_conflicts_as_conflicting.exclude(id__in=still_conflicting_conflicts)
         resolved_conflicts.delete()
 
         all_conflicted_by_conflicts = self.conflicted_by_geometries.all()
         still_conflicting_conflicts = all_conflicted_by_conflicts.filter(
-            conflicting_geometry__geom__intersects=self.geom
+            conflicting_geometry__geom__intersects=self.geom_small_buffered
         )
         resolved_conflicts = all_conflicted_by_conflicts.exclude(id__in=still_conflicting_conflicts)
         resolved_conflicts.delete()
diff --git a/konova/utils/wfs/spatial.py b/konova/utils/wfs/spatial.py
index 5578c35a..a3d4bd62 100644
--- a/konova/utils/wfs/spatial.py
+++ b/konova/utils/wfs/spatial.py
@@ -91,7 +91,7 @@ class ParcelWFSFetcher(AbstractWFSFetcher):
         geom = Geometry.objects.filter(
             id=self.geometry_id
         ).annotate(
-            smaller=Func(F('geom'), -0.001, function="ST_Buffer")
+            smaller=Func(F('geom'), -0.001, function="ST_Buffer")  # same as geometry.geom_small_buffered but for QuerySet
         ).annotate(
             gml=AsGML(MakeValid('smaller'))
         ).first()