Merge pull request '50_Overlaying_geometries' (#52) from 50_Overlaying_geometries into master

Reviewed-on: SGD-Nord/konova#52
This commit is contained in:
Michel Peltriaux 2021-12-16 10:02:47 +01:00
commit 46f8a81f2c
18 changed files with 348 additions and 91 deletions

View File

@ -2,6 +2,7 @@ from django.contrib import admin
from compensation.models import Compensation, CompensationAction, CompensationState, Payment, \
EcoAccountDeduction, EcoAccount
from konova.admin import BaseObjectAdmin
class CompensationStateAdmin(admin.ModelAdmin):
@ -22,7 +23,7 @@ class CompensationActionAdmin(admin.ModelAdmin):
]
class CompensationAdmin(admin.ModelAdmin):
class CompensationAdmin(BaseObjectAdmin):
list_display = [
"id",
"identifier",

View File

@ -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
@ -16,12 +17,14 @@ from django.utils.translation import gettext_lazy as _
from compensation.managers import CompensationManager
from compensation.models import CompensationState, CompensationAction
from compensation.utils.quality import CompensationQualityChecker
from konova.models import BaseObject, AbstractDocument, Geometry, Deadline, generate_document_file_upload_path
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
class AbstractCompensation(BaseObject):
class AbstractCompensation(BaseObject, GeoReferencedMixin):
"""
Abstract compensation model which holds basic attributes, shared by subclasses like the regular Compensation,
EMA or EcoAccount.
@ -41,8 +44,6 @@ class AbstractCompensation(BaseObject):
deadlines = models.ManyToManyField("konova.Deadline", blank=True, related_name="+")
geometry = models.ForeignKey(Geometry, null=True, blank=True, on_delete=models.SET_NULL)
class Meta:
abstract = True
@ -156,6 +157,22 @@ class AbstractCompensation(BaseObject):
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

View File

@ -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,

View File

@ -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,

View File

@ -7,15 +7,17 @@ Created on: 15.11.21
"""
import shutil
from django.contrib.auth.models import User
from django.contrib import messages
from django.db import models
from django.db.models import QuerySet
from django.http import HttpRequest
from compensation.models import AbstractCompensation
from ema.managers import EmaManager
from ema.utils.quality import EmaQualityChecker
from konova.models import AbstractDocument, generate_document_file_upload_path, RecordableObjectMixin, ShareableObjectMixin
from konova.settings import DEFAULT_SRID_RLP, LANIS_LINK_TEMPLATE
from konova.utils.message_templates import DATA_UNSHARED_EXPLANATION
class Ema(AbstractCompensation, ShareableObjectMixin, RecordableObjectMixin):
@ -91,6 +93,22 @@ 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_geometry_conflict_message(request)
return request
class EmaDocument(AbstractDocument):
"""

View File

@ -138,8 +138,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)
ema.set_status_messages(request)
context = {
"obj": ema,

View File

@ -1,10 +1,10 @@
from django.contrib import admin
from intervention.models import Intervention, Responsibility, Legal, Revocation, InterventionDocument
from konova.admin import AbstractDocumentAdmin
from konova.admin import AbstractDocumentAdmin, BaseObjectAdmin
class InterventionAdmin(admin.ModelAdmin):
class InterventionAdmin(BaseObjectAdmin):
list_display = [
"id",
"identifier",
@ -13,9 +13,11 @@ class InterventionAdmin(admin.ModelAdmin):
"deleted",
]
class InterventionDocumentAdmin(AbstractDocumentAdmin):
pass
class ResponsibilityAdmin(admin.ModelAdmin):
list_display = [
"id",

View File

@ -7,6 +7,7 @@ Created on: 15.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
@ -18,13 +19,15 @@ from intervention.models.legal import Legal
from intervention.models.responsibility import Responsibility
from intervention.models.revocation import RevocationDocument, Revocation
from intervention.utils.quality import InterventionQualityChecker
from konova.models import generate_document_file_upload_path, AbstractDocument, Geometry, BaseObject, ShareableObjectMixin, \
RecordableObjectMixin, CheckableObjectMixin
from konova.models import generate_document_file_upload_path, AbstractDocument, BaseObject, \
ShareableObjectMixin, \
RecordableObjectMixin, CheckableObjectMixin, GeoReferencedMixin
from konova.settings import LANIS_LINK_TEMPLATE, LANIS_ZOOM_LUT, DEFAULT_SRID_RLP
from konova.utils.message_templates import DATA_UNSHARED_EXPLANATION
from user.models import UserActionLogEntry
class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, CheckableObjectMixin):
class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, CheckableObjectMixin, GeoReferencedMixin):
"""
Interventions are e.g. construction sites where nature used to be.
"""
@ -42,7 +45,6 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec
blank=True,
help_text="Holds data on legal dates or law"
)
geometry = models.ForeignKey(Geometry, null=True, blank=True, on_delete=models.SET_NULL)
objects = InterventionManager()
@ -263,6 +265,22 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec
if self.checked:
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_geometry_conflict_message(request)
return request
class InterventionDocument(AbstractDocument):
"""

View File

@ -255,8 +255,7 @@ def detail_view(request: HttpRequest, id: str):
"LANIS_LINK": intervention.get_LANIS_link()
}
if not is_data_shared:
messages.info(request, DATA_UNSHARED_EXPLANATION)
request = intervention.set_status_messages(request)
context = BaseContext(request, context).context
return render(request, template, context)

View File

@ -7,7 +7,7 @@ Created on: 22.07.21
"""
from django.contrib import admin
from konova.models import Geometry, Deadline
from konova.models import Geometry, Deadline, GeometryConflict
class GeometryAdmin(admin.ModelAdmin):
@ -17,6 +17,14 @@ class GeometryAdmin(admin.ModelAdmin):
]
class GeometryConflictAdmin(admin.ModelAdmin):
list_display = [
"conflicting_geometry",
"affected_geometry",
"detected_on",
]
class AbstractDocumentAdmin(admin.ModelAdmin):
list_display = [
"id",
@ -35,5 +43,14 @@ class DeadlineAdmin(admin.ModelAdmin):
]
class BaseObjectAdmin(admin.ModelAdmin):
readonly_fields = [
"modified",
"deleted",
"created",
]
admin.site.register(Geometry, GeometryAdmin)
admin.site.register(GeometryConflict, GeometryConflictAdmin)
admin.site.register(Deadline, DeadlineAdmin)

View File

@ -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)),

View File

@ -208,7 +208,7 @@ class Command(BaseKonovaCommand):
if num_entries > 0:
self._write_error(f"Found {num_entries} geometries not attached to anything. Delete now...")
unattached_geometries.delete()
self._write_success("Deadlines deleted.")
self._write_success("Geometries deleted.")
else:
self._write_success("No unattached geometries found.")
self._break_line()

View File

@ -6,13 +6,110 @@ 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
from konova.models import BaseResource, UuidModel
class Geometry(BaseResource):
"""
Outsourced geometry model so multiple versions of the same object can refer to the same geometry if it is not changed
Geometry model
"""
from konova.settings import DEFAULT_SRID
geom = MultiPolygonField(null=True, blank=True, srid=DEFAULT_SRID)
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
self.check_for_conflicts()
def check_for_conflicts(self):
""" Checks for new geometry overlaps
Creates a new GeometryConflict entry for each overlap to another geometry, which has already been there before
Returns:
"""
# If no geometry is given or important data is missing, we can not perform any checks
if self.geom is None or (self.created is None and self.modified is None):
return None
self.recheck_existing_conflicts()
overlapping_geoms = Geometry.objects.filter(
geom__intersects=self.geom,
).exclude(
id=self.id
).distinct()
for match in overlapping_geoms:
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()
def get_data_objects(self):
""" Getter for all objects which are related to this geometry
Returns:
objs (list): The list of objects
"""
objs = []
sets = [
self.intervention_set,
self.compensation_set,
self.ema_set,
self.ecoaccount_set,
]
for _set in sets:
set_objs = _set.filter(
deleted=None
)
objs += set_objs
return objs
class GeometryConflict(UuidModel):
"""
Geometry conflicts model
If a new/edited geometry overlays an existing geometry, there will be a new GeometryConflict on the db
"""
conflicting_geometry = models.ForeignKey(
Geometry,
on_delete=models.CASCADE,
help_text="The geometry which came second",
related_name="conflicts_geometries"
)
affected_geometry = models.ForeignKey(
Geometry,
on_delete=models.CASCADE,
help_text="The geometry which came first",
related_name="conflicted_by_geometries"
)
detected_on = models.DateTimeField(auto_now_add=True, null=True)
def __str__(self):
return f"{self.conflicting_geometry.id} conflicts with {self.affected_geometry.id}"

View File

@ -7,6 +7,7 @@ Created on: 15.11.21
"""
import uuid
from abc import abstractmethod
from django.contrib import messages
from django.contrib.auth.models import User
@ -20,7 +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
from konova.utils.message_templates import CHECKED_RECORDED_RESET, GEOMETRY_CONFLICT_WITH_TEMPLATE
from user.models import UserActionLogEntry, UserAction
@ -94,6 +95,10 @@ class BaseObject(BaseResource):
class Meta:
abstract = True
@abstractmethod
def set_status_messages(self, request: HttpRequest):
raise NotImplementedError
def mark_as_deleted(self, user: User):
""" Mark an entry as deleted
@ -407,3 +412,29 @@ class ShareableObjectMixin(models.Model):
id__in=accessing_users
)
self.share_with_list(users)
class GeoReferencedMixin(models.Model):
geometry = models.ForeignKey("konova.Geometry", null=True, blank=True, on_delete=models.SET_NULL)
class Meta:
abstract = True
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.affected_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

View File

@ -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)

View File

@ -26,3 +26,6 @@ EDITED_GENERAL_DATA = _("Edited general data")
ADDED_COMPENSATION_STATE = _("Added compensation state")
ADDED_DEADLINE = _("Added deadline")
ADDED_COMPENSATION_ACTION = _("Added compensation action")
# Geometry conflicts
GEOMETRY_CONFLICT_WITH_TEMPLATE = _("Geometry conflict detected with {}")

Binary file not shown.

View File

@ -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 <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\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"