diff --git a/konova/admin.py b/konova/admin.py index e62a8cff..213e0412 100644 --- a/konova/admin.py +++ b/konova/admin.py @@ -20,7 +20,7 @@ class GeometryAdmin(admin.ModelAdmin): class GeometryConflictAdmin(admin.ModelAdmin): list_display = [ "conflicting_geometry", - "existing_geometry", + "affected_geometry", "detected_on", ] diff --git a/konova/models/geometry.py b/konova/models/geometry.py index 7dddcf0e..8c155732 100644 --- a/konova/models/geometry.py +++ b/konova/models/geometry.py @@ -24,7 +24,7 @@ class Geometry(BaseResource): self.check_for_conflicts() def check_for_conflicts(self): - """ Checks for geometry overlaps + """ Checks for new geometry overlaps Creates a new GeometryConflict entry for each overlap to another geometry, which has already been there before @@ -35,27 +35,40 @@ class Geometry(BaseResource): if self.geom is None or (self.created is None and self.modified is None): return None - check_timestamp_obj = self.modified or self.created - ts = check_timestamp_obj.timestamp + self.recheck_existing_conflicts() + overlapping_geoms = Geometry.objects.filter( - 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() for match in overlapping_geoms: - GeometryConflict.objects.get_or_create(conflicting_geometry=self, existing_geometry=match) + GeometryConflict.objects.get_or_create(conflicting_geometry=self, affected_geometry=match) + + def recheck_existing_conflicts(self): + """ Rechecks GeometryConflict entries + + If a conflict seems to be resolved due to no longer intersection between the two geometries, the entry + will be deleted. + + Returns: + + """ + all_conflicts_as_conflicting = self.conflicts_geometries.all() + still_conflicting_conflicts = all_conflicts_as_conflicting.filter( + affected_geometry__geom__intersects=self.geom + ) + 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 + ) + resolved_conflicts = all_conflicted_by_conflicts.exclude(id__in=still_conflicting_conflicts) + resolved_conflicts.delete() - # Rerun the conflict check for all conflicts where this object is not the cause but the one that already existed. - # It may be possible that this object has been edited, so the conflicts would be resolved in the newer entries. - existing_conflicts = self.conflicted_by_geometries.all() - for conflict in existing_conflicts: - conflicting_geom = conflict.conflicting_geometry - conflicting_geom.check_for_conflicts() def get_data_objects(self): """ Getter for all objects which are related to this geometry @@ -90,7 +103,7 @@ class GeometryConflict(UuidModel): help_text="The geometry which came second", related_name="conflicts_geometries" ) - existing_geometry = models.ForeignKey( + affected_geometry = models.ForeignKey( Geometry, on_delete=models.CASCADE, help_text="The geometry which came first", @@ -99,4 +112,4 @@ class GeometryConflict(UuidModel): detected_on = models.DateTimeField(auto_now_add=True, null=True) def __str__(self): - return f"{self.conflicting_geometry.id} conflicts with {self.existing_geometry.id}" + return f"{self.conflicting_geometry.id} conflicts with {self.affected_geometry.id}" diff --git a/konova/models/object.py b/konova/models/object.py index 764e7079..ded39ad1 100644 --- a/konova/models/object.py +++ b/konova/models/object.py @@ -425,7 +425,7 @@ class GeoReferencedMixin(models.Model): add_message = False conflicts = self.geometry.conflicts_geometries.all() for conflict in conflicts: - instance_objs += conflict.existing_geometry.get_data_objects() + instance_objs += conflict.affected_geometry.get_data_objects() add_message = True conflicts = self.geometry.conflicted_by_geometries.all() for conflict in conflicts: diff --git a/konova/tests/test_geometries.py b/konova/tests/test_geometries.py new file mode 100644 index 00000000..a9a840f5 --- /dev/null +++ b/konova/tests/test_geometries.py @@ -0,0 +1,49 @@ +""" +Author: Michel Peltriaux +Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany +Contact: michel.peltriaux@sgdnord.rlp.de +Created on: 15.12.21 + +""" +from django.contrib.gis.db.models.functions import Translate + +from konova.models import Geometry, GeometryConflict +from konova.tests.test_views import BaseTestCase +from user.models import UserActionLogEntry + + +class GeometryTestCase(BaseTestCase): + @classmethod + def setUpTestData(cls): + super().setUpTestData() + geom = cls.create_dummy_geometry() + action = UserActionLogEntry.get_created_action(cls.superuser) + cls.geom_1 = Geometry.objects.create( + geom=geom, + created=action, + ) + cls.geom_2 = Geometry.objects.create( + geom=geom, + created=action, + ) + + def test_geometry_conflict(self): + """ Tests whether a geometry conflict will be present in case of identical/overlaying geometries and + if the conflict will be resolved if one geometry is edited. + + Returns: + + """ + self.geom_1.check_for_conflicts() + conflict = GeometryConflict.objects.all().first() + self.assertEqual(conflict.conflicting_geometry, self.geom_2) + self.assertEqual(conflict.affected_geometry, self.geom_1) + + # Move geom_2 somewhere else, expect the conflict to be resolved + Geometry.objects.filter(id=self.geom_2.id).update( + geom=Translate('geom', 100000, 100000) + ) + self.geom_2.refresh_from_db() + self.geom_1.check_for_conflicts() + num_conflict = GeometryConflict.objects.all().count() + self.assertEqual(0, num_conflict) diff --git a/locale/de/LC_MESSAGES/django.mo b/locale/de/LC_MESSAGES/django.mo index 94f1f426..bd3e82d8 100644 Binary files a/locale/de/LC_MESSAGES/django.mo and b/locale/de/LC_MESSAGES/django.mo differ diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index fdf4fc98..04aefdf0 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -11,15 +11,15 @@ #: intervention/forms/forms.py:52 intervention/forms/forms.py:154 #: intervention/forms/forms.py:166 intervention/forms/modalForms.py:125 #: intervention/forms/modalForms.py:138 intervention/forms/modalForms.py:151 -#: konova/forms.py:139 konova/forms.py:240 konova/forms.py:306 -#: konova/forms.py:333 konova/forms.py:343 konova/forms.py:356 -#: konova/forms.py:368 konova/forms.py:386 user/forms.py:38 +#: konova/forms.py:139 konova/forms.py:240 konova/forms.py:308 +#: konova/forms.py:335 konova/forms.py:345 konova/forms.py:358 +#: konova/forms.py:370 konova/forms.py:388 user/forms.py:38 #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-12-09 12:36+0100\n" +"POT-Creation-Date: 2021-12-16 09:17+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -329,7 +329,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:332 +#: konova/forms.py:334 msgid "Title" msgstr "Bezeichnung" @@ -356,7 +356,7 @@ msgstr "Kompensation XY; Flur ABC" #: intervention/templates/intervention/detail/includes/documents.html:31 #: intervention/templates/intervention/detail/includes/payments.html:34 #: intervention/templates/intervention/detail/includes/revocation.html:38 -#: konova/forms.py:367 konova/templates/konova/comment_card.html:16 +#: konova/forms.py:369 konova/templates/konova/comment_card.html:16 msgid "Comment" msgstr "Kommentar" @@ -472,7 +472,7 @@ msgstr "Zahlung wird an diesem Datum erwartet" #: compensation/forms/modalForms.py:62 compensation/forms/modalForms.py:239 #: compensation/forms/modalForms.py:317 intervention/forms/modalForms.py:152 -#: konova/forms.py:369 +#: konova/forms.py:371 msgid "Additional comment, maximum {} letters" msgstr "Zusätzlicher Kommentar, maximal {} Zeichen" @@ -614,7 +614,7 @@ msgstr "" msgid "Pieces" msgstr "Stück" -#: compensation/models/compensation.py:62 konova/utils/message_templates.py:27 +#: compensation/models/compensation.py:63 konova/utils/message_templates.py:27 msgid "Added deadline" msgstr "Frist/Termin hinzugefügt" @@ -793,7 +793,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:385 +#: konova/forms.py:387 msgid "Add new document" msgstr "Neues Dokument hinzufügen" @@ -1056,42 +1056,42 @@ msgstr "Kompensation {} hinzugefügt" msgid "Compensation {} edited" msgstr "Kompensation {} bearbeitet" -#: compensation/views/compensation.py:229 compensation/views/eco_account.py:308 -#: ema/views.py:182 intervention/views.py:475 +#: compensation/views/compensation.py:228 compensation/views/eco_account.py:307 +#: ema/views.py:181 intervention/views.py:474 msgid "Log" msgstr "Log" -#: compensation/views/compensation.py:252 +#: compensation/views/compensation.py:251 msgid "Compensation removed" msgstr "Kompensation entfernt" -#: compensation/views/compensation.py:273 compensation/views/eco_account.py:460 -#: ema/views.py:349 intervention/views.py:129 +#: compensation/views/compensation.py:272 compensation/views/eco_account.py:459 +#: ema/views.py:348 intervention/views.py:129 msgid "Document added" msgstr "Dokument hinzugefügt" -#: compensation/views/compensation.py:342 compensation/views/eco_account.py:354 -#: ema/views.py:287 +#: compensation/views/compensation.py:341 compensation/views/eco_account.py:353 +#: ema/views.py:286 msgid "State added" msgstr "Zustand hinzugefügt" -#: compensation/views/compensation.py:363 compensation/views/eco_account.py:375 -#: ema/views.py:308 +#: compensation/views/compensation.py:362 compensation/views/eco_account.py:374 +#: ema/views.py:307 msgid "Action added" msgstr "Maßnahme hinzugefügt" -#: compensation/views/compensation.py:384 compensation/views/eco_account.py:440 -#: ema/views.py:329 +#: compensation/views/compensation.py:383 compensation/views/eco_account.py:439 +#: ema/views.py:328 msgid "Deadline added" msgstr "Frist/Termin hinzugefügt" -#: compensation/views/compensation.py:406 compensation/views/eco_account.py:397 -#: ema/views.py:419 +#: compensation/views/compensation.py:405 compensation/views/eco_account.py:396 +#: ema/views.py:418 msgid "State removed" msgstr "Zustand gelöscht" -#: compensation/views/compensation.py:428 compensation/views/eco_account.py:419 -#: ema/views.py:441 +#: compensation/views/compensation.py:427 compensation/views/eco_account.py:418 +#: ema/views.py:440 msgid "Action removed" msgstr "Maßnahme entfernt" @@ -1103,45 +1103,45 @@ msgstr "Ökokonto {} hinzugefügt" msgid "Eco-Account {} edited" msgstr "Ökokonto {} bearbeitet" -#: compensation/views/eco_account.py:256 +#: compensation/views/eco_account.py:255 msgid "Eco-account removed" msgstr "Ökokonto entfernt" -#: compensation/views/eco_account.py:284 +#: compensation/views/eco_account.py:283 msgid "Deduction removed" msgstr "Abbuchung entfernt" -#: compensation/views/eco_account.py:329 ema/views.py:262 -#: intervention/views.py:517 +#: compensation/views/eco_account.py:328 ema/views.py:261 +#: intervention/views.py:516 msgid "{} unrecorded" msgstr "{} entzeichnet" -#: compensation/views/eco_account.py:329 ema/views.py:262 -#: intervention/views.py:517 +#: compensation/views/eco_account.py:328 ema/views.py:261 +#: intervention/views.py:516 msgid "{} recorded" msgstr "{} verzeichnet" -#: compensation/views/eco_account.py:530 intervention/views.py:498 +#: compensation/views/eco_account.py:529 intervention/views.py:497 msgid "Deduction added" msgstr "Abbuchung hinzugefügt" -#: compensation/views/eco_account.py:613 ema/views.py:517 -#: intervention/views.py:373 +#: compensation/views/eco_account.py:612 ema/views.py:516 +#: intervention/views.py:372 msgid "{} has already been shared with you" msgstr "{} wurde bereits für Sie freigegeben" -#: compensation/views/eco_account.py:618 ema/views.py:522 -#: intervention/views.py:378 +#: compensation/views/eco_account.py:617 ema/views.py:521 +#: intervention/views.py:377 msgid "{} has been shared with you" msgstr "{} ist nun für Sie freigegeben" -#: compensation/views/eco_account.py:625 ema/views.py:529 -#: intervention/views.py:385 +#: compensation/views/eco_account.py:624 ema/views.py:528 +#: intervention/views.py:384 msgid "Share link invalid" msgstr "Freigabelink ungültig" -#: compensation/views/eco_account.py:648 ema/views.py:552 -#: intervention/views.py:408 +#: compensation/views/eco_account.py:647 ema/views.py:551 +#: intervention/views.py:407 msgid "Share settings updated" msgstr "Freigabe Einstellungen aktualisiert" @@ -1185,11 +1185,11 @@ msgstr "Ersatzzahlungsmaßnahme" msgid "EMA {} added" msgstr "EMA {} hinzugefügt" -#: ema/views.py:211 +#: ema/views.py:210 msgid "EMA {} edited" msgstr "EMA {} bearbeitet" -#: ema/views.py:243 +#: ema/views.py:242 msgid "EMA removed" msgstr "EMA entfernt" @@ -1333,7 +1333,7 @@ msgstr "Kompensationen und Zahlungen geprüft" msgid "Run check" msgstr "Prüfung vornehmen" -#: intervention/forms/modalForms.py:196 konova/forms.py:451 +#: intervention/forms/modalForms.py:196 konova/forms.py:453 msgid "" "I, {} {}, confirm that all necessary control steps have been performed by " "myself." @@ -1476,27 +1476,27 @@ msgstr "Eingriff {} hinzugefügt" msgid "This intervention has {} revocations" msgstr "Dem Eingriff liegen {} Widersprüche vor" -#: intervention/views.py:291 +#: intervention/views.py:290 msgid "Intervention {} edited" msgstr "Eingriff {} bearbeitet" -#: intervention/views.py:326 +#: intervention/views.py:325 msgid "{} removed" msgstr "{} entfernt" -#: intervention/views.py:347 +#: intervention/views.py:346 msgid "Revocation removed" msgstr "Widerspruch entfernt" -#: intervention/views.py:429 +#: intervention/views.py:428 msgid "Check performed" msgstr "Prüfung durchgeführt" -#: intervention/views.py:451 +#: intervention/views.py:450 msgid "Revocation added" msgstr "Widerspruch hinzugefügt" -#: intervention/views.py:522 +#: intervention/views.py:521 msgid "There are errors on this intervention:" msgstr "Es liegen Fehler in diesem Eingriff vor:" @@ -1525,11 +1525,11 @@ msgstr "Speichern" msgid "Not editable" msgstr "Nicht editierbar" -#: konova/forms.py:138 konova/forms.py:305 +#: konova/forms.py:138 konova/forms.py:307 msgid "Confirm" msgstr "Bestätige" -#: konova/forms.py:150 konova/forms.py:314 +#: konova/forms.py:150 konova/forms.py:316 msgid "Remove" msgstr "Löschen" @@ -1542,56 +1542,56 @@ msgstr "Sie sind dabei {} {} zu löschen" msgid "Geometry" msgstr "Geometrie" -#: konova/forms.py:315 +#: konova/forms.py:317 msgid "Are you sure?" msgstr "Sind Sie sicher?" -#: konova/forms.py:342 +#: konova/forms.py:344 msgid "Created on" msgstr "Erstellt" -#: konova/forms.py:344 +#: konova/forms.py:346 msgid "When has this file been created? Important for photos." msgstr "Wann wurde diese Datei erstellt oder das Foto aufgenommen?" -#: konova/forms.py:355 +#: konova/forms.py:357 #: venv/lib/python3.7/site-packages/django/db/models/fields/files.py:231 msgid "File" msgstr "Datei" -#: konova/forms.py:357 +#: konova/forms.py:359 msgid "Allowed formats: pdf, jpg, png. Max size 15 MB." msgstr "Formate: pdf, jpg, png. Maximal 15 MB." -#: konova/forms.py:403 +#: konova/forms.py:405 msgid "Unsupported file type" msgstr "Dateiformat nicht unterstützt" -#: konova/forms.py:410 +#: konova/forms.py:412 msgid "File too large" msgstr "Datei zu groß" -#: konova/forms.py:419 +#: konova/forms.py:421 msgid "Added document" msgstr "Dokument hinzugefügt" -#: konova/forms.py:442 +#: konova/forms.py:444 msgid "Confirm record" msgstr "Verzeichnen bestätigen" -#: konova/forms.py:450 +#: konova/forms.py:452 msgid "Record data" msgstr "Daten verzeichnen" -#: konova/forms.py:457 +#: konova/forms.py:459 msgid "Confirm unrecord" msgstr "Entzeichnen bestätigen" -#: konova/forms.py:458 +#: konova/forms.py:460 msgid "Unrecord data" msgstr "Daten entzeichnen" -#: konova/forms.py:459 +#: konova/forms.py:461 msgid "I, {} {}, confirm that this data must be unrecorded." msgstr "" "Ich, {} {}, bestätige, dass diese Daten wieder entzeichnet werden müssen." @@ -1726,6 +1726,8 @@ msgid "" "Action canceled. Eco account is recorded or deductions exist. Only " "conservation office member can perform this action." msgstr "" +"Aktion abgebrochen. Ökokonto ist bereits verzeichnet oder Abbuchungen liegen vor. Nur " +"Eintragungsstellennutzer können diese Aktion jetzt durchführen." #: konova/utils/message_templates.py:25 msgid "Edited general data" @@ -1739,6 +1741,10 @@ msgstr "Zustand hinzugefügt" msgid "Added compensation action" msgstr "Maßnahme hinzufügen" +#: konova/utils/message_templates.py:31 +msgid "Geometry conflict detected with {}" +msgstr "Geometriekonflikt mit folgenden Einträgen erkannt: {}" + #: konova/utils/messenger.py:69 msgid "{} checked" msgstr "{} geprüft"