From 41af455d09c881a803f3757b4a02fe724dea6b58 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Wed, 15 Dec 2021 15:10:35 +0100 Subject: [PATCH] #50 Overlaying geometries KOM + OEK * removes unused messages * adds geometry conflict message rendering for KOM and OEK * removes unused methods in GeoReferencedMixin * generalizes geometrical lookup for conflicts from overlaps to intersects --- compensation/models/compensation.py | 18 +++++++++++++ compensation/views/compensation.py | 3 +-- compensation/views/eco_account.py | 3 +-- ema/models/ema.py | 13 ++++++++-- intervention/models/intervention.py | 13 ++++++++-- konova/forms.py | 4 ++- konova/models/geometry.py | 10 +++++--- konova/models/object.py | 40 ++++++++++++----------------- konova/utils/message_templates.py | 3 +-- 9 files changed, 70 insertions(+), 37 deletions(-) diff --git a/compensation/models/compensation.py b/compensation/models/compensation.py index 1759523b..703f7cd4 100644 --- a/compensation/models/compensation.py +++ b/compensation/models/compensation.py @@ -7,6 +7,7 @@ Created on: 16.11.21 """ import shutil +from django.contrib import messages from django.contrib.auth.models import User from django.db import models, transaction from django.db.models import QuerySet, Sum @@ -19,6 +20,7 @@ from compensation.utils.quality import CompensationQualityChecker from konova.models import BaseObject, AbstractDocument, Deadline, generate_document_file_upload_path, \ GeoReferencedMixin from konova.settings import DEFAULT_SRID_RLP, LANIS_LINK_TEMPLATE +from konova.utils.message_templates import DATA_UNSHARED_EXPLANATION from user.models import UserActionLogEntry @@ -155,6 +157,22 @@ class AbstractCompensation(BaseObject, GeoReferencedMixin): checker.run_check() return checker + def set_status_messages(self, request: HttpRequest): + """ Setter for different information that need to be rendered + + Adds messages to the given HttpRequest + + Args: + request (HttpRequest): The incoming request + + Returns: + request (HttpRequest): The modified request + """ + if not self.is_shared_with(request.user): + messages.info(request, DATA_UNSHARED_EXPLANATION) + request = self._set_geometry_conflict_message(request) + return request + class CEFMixin(models.Model): """ Provides CEF flag as Mixin diff --git a/compensation/views/compensation.py b/compensation/views/compensation.py index 678504e3..b5efc984 100644 --- a/compensation/views/compensation.py +++ b/compensation/views/compensation.py @@ -184,8 +184,7 @@ def detail_view(request: HttpRequest, id: str): sum_after_states = after_states.aggregate(Sum("surface"))["surface__sum"] or 0 diff_states = abs(sum_before_states - sum_after_states) - if not is_data_shared: - messages.info(request, DATA_UNSHARED_EXPLANATION) + request = comp.set_status_messages(request) context = { "obj": comp, diff --git a/compensation/views/eco_account.py b/compensation/views/eco_account.py index 7b7d1eae..7b4c27e2 100644 --- a/compensation/views/eco_account.py +++ b/compensation/views/eco_account.py @@ -202,8 +202,7 @@ def detail_view(request: HttpRequest, id: str): ) actions = acc.actions.all() - if not is_data_shared: - messages.info(request, DATA_UNSHARED_EXPLANATION) + request = acc.set_status_messages(request) context = { "obj": acc, diff --git a/ema/models/ema.py b/ema/models/ema.py index e6d87f3f..8bc4ac65 100644 --- a/ema/models/ema.py +++ b/ema/models/ema.py @@ -94,10 +94,19 @@ class Ema(AbstractCompensation, ShareableObjectMixin, RecordableObjectMixin): return docs def set_status_messages(self, request: HttpRequest): + """ Setter for different information that need to be rendered + + Adds messages to the given HttpRequest + + Args: + request (HttpRequest): The incoming request + + Returns: + request (HttpRequest): The modified request + """ if not self.is_shared_with(request.user): messages.info(request, DATA_UNSHARED_EXPLANATION) - self._set_overlapped_by_message(request) - self._set_overlapping_message(request) + self._set_geometry_conflict_message(request) return request diff --git a/intervention/models/intervention.py b/intervention/models/intervention.py index ef7f6040..a31eb9a3 100644 --- a/intervention/models/intervention.py +++ b/intervention/models/intervention.py @@ -266,10 +266,19 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec self.set_unchecked() def set_status_messages(self, request: HttpRequest): + """ Setter for different information that need to be rendered + + Adds messages to the given HttpRequest + + Args: + request (HttpRequest): The incoming request + + Returns: + request (HttpRequest): The modified request + """ if not self.is_shared_with(request.user): messages.info(request, DATA_UNSHARED_EXPLANATION) - request = self._set_overlapping_message(request) - request = self._set_overlapped_by_message(request) + request = self._set_geometry_conflict_message(request) return request diff --git a/konova/forms.py b/konova/forms.py index 359b78bc..43c83498 100644 --- a/konova/forms.py +++ b/konova/forms.py @@ -282,11 +282,13 @@ class SimpleGeomForm(BaseForm): """ try: + if self.instance is None or self.instance.geometry is None: + raise LookupError geometry = self.instance.geometry geometry.geom = self.cleaned_data.get("geom", MultiPolygon(srid=DEFAULT_SRID)) geometry.modified = action geometry.save() - except AttributeError: + except LookupError: # No geometry or linked instance holding a geometry exist --> create a new one! geometry = Geometry.objects.create( geom=self.cleaned_data.get("geom", MultiPolygon(srid=DEFAULT_SRID)), diff --git a/konova/models/geometry.py b/konova/models/geometry.py index db36c03b..7dddcf0e 100644 --- a/konova/models/geometry.py +++ b/konova/models/geometry.py @@ -7,6 +7,7 @@ Created on: 15.11.21 """ from django.contrib.gis.db.models import MultiPolygonField from django.db import models +from django.db.models import Q from konova.models import BaseResource, UuidModel @@ -37,9 +38,12 @@ class Geometry(BaseResource): check_timestamp_obj = self.modified or self.created ts = check_timestamp_obj.timestamp overlapping_geoms = Geometry.objects.filter( - modified__timestamp__lte=ts, - geom__overlaps=self.geom, - ) + Q(modified__timestamp__lte=ts) | + Q(created__timestamp__lte=ts), + geom__intersects=self.geom, + ).exclude( + id=self.id + ).distinct() # Drop known conflicts for this object to replace with new ones self.conflicts_geometries.all().delete() diff --git a/konova/models/object.py b/konova/models/object.py index b1e78f8d..764e7079 100644 --- a/konova/models/object.py +++ b/konova/models/object.py @@ -21,8 +21,7 @@ from ema.settings import EMA_ACCOUNT_IDENTIFIER_LENGTH, EMA_ACCOUNT_IDENTIFIER_T from intervention.settings import INTERVENTION_IDENTIFIER_LENGTH, INTERVENTION_IDENTIFIER_TEMPLATE from konova.utils import generators from konova.utils.generators import generate_random_string -from konova.utils.message_templates import CHECKED_RECORDED_RESET, GEOMETRY_OVERLAPS_WITH_TEMPLATE, \ - GEOMETRY_OVERLAPPED_BY_TEMPLATE +from konova.utils.message_templates import CHECKED_RECORDED_RESET, GEOMETRY_CONFLICT_WITH_TEMPLATE from user.models import UserActionLogEntry, UserAction @@ -421,26 +420,21 @@ class GeoReferencedMixin(models.Model): class Meta: abstract = True - def _set_overlapping_message(self, request: HttpRequest): - geom_conflicts = self.geometry.conflicts_geometries.all() - if geom_conflicts: - data_objs = [] - for conflict in geom_conflicts: - data_objs += conflict.existing_geometry.get_data_objects() - data_identifiers = [x.identifier for x in data_objs] - data_identifiers = ", ".join(data_identifiers) - message_str = GEOMETRY_OVERLAPS_WITH_TEMPLATE.format(data_identifiers) + def _set_geometry_conflict_message(self, request: HttpRequest): + instance_objs = [] + add_message = False + conflicts = self.geometry.conflicts_geometries.all() + for conflict in conflicts: + instance_objs += conflict.existing_geometry.get_data_objects() + add_message = True + conflicts = self.geometry.conflicted_by_geometries.all() + for conflict in conflicts: + instance_objs += conflict.conflicting_geometry.get_data_objects() + add_message = True + + if add_message: + instance_identifiers = [x.identifier for x in instance_objs] + instance_identifiers = ", ".join(instance_identifiers) + message_str = GEOMETRY_CONFLICT_WITH_TEMPLATE.format(instance_identifiers) messages.info(request, message_str) return request - - def _set_overlapped_by_message(self, request: HttpRequest): - geom_conflicts = self.geometry.conflicted_by_geometries.all() - if geom_conflicts: - data_objs = [] - for conflict in geom_conflicts: - data_objs += conflict.conflicting_geometry.get_data_objects() - data_identifiers = [x.identifier for x in data_objs] - data_identifiers = ", ".join(data_identifiers) - message_str = GEOMETRY_OVERLAPPED_BY_TEMPLATE.format(data_identifiers) - messages.info(request, message_str) - return request \ No newline at end of file diff --git a/konova/utils/message_templates.py b/konova/utils/message_templates.py index 260435ff..29f8197c 100644 --- a/konova/utils/message_templates.py +++ b/konova/utils/message_templates.py @@ -28,5 +28,4 @@ ADDED_DEADLINE = _("Added deadline") ADDED_COMPENSATION_ACTION = _("Added compensation action") # Geometry conflicts -GEOMETRY_OVERLAPS_WITH_TEMPLATE = _("Geometry overlaps {}") -GEOMETRY_OVERLAPPED_BY_TEMPLATE = _("Geometry overlapped by {}") +GEOMETRY_CONFLICT_WITH_TEMPLATE = _("Geometry conflict detected with {}")