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 44dbee0008
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, \ from compensation.models import Compensation, CompensationAction, CompensationState, Payment, \
EcoAccountDeduction, EcoAccount EcoAccountDeduction, EcoAccount
from konova.admin import BaseObjectAdmin
class CompensationStateAdmin(admin.ModelAdmin): class CompensationStateAdmin(admin.ModelAdmin):
@ -22,7 +23,7 @@ class CompensationActionAdmin(admin.ModelAdmin):
] ]
class CompensationAdmin(admin.ModelAdmin): class CompensationAdmin(BaseObjectAdmin):
list_display = [ list_display = [
"id", "id",
"identifier", "identifier",

View File

@ -7,6 +7,7 @@ Created on: 16.11.21
""" """
import shutil import shutil
from django.contrib import messages
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db import models, transaction from django.db import models, transaction
from django.db.models import QuerySet, Sum 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.managers import CompensationManager
from compensation.models import CompensationState, CompensationAction from compensation.models import CompensationState, CompensationAction
from compensation.utils.quality import CompensationQualityChecker 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.settings import DEFAULT_SRID_RLP, LANIS_LINK_TEMPLATE
from konova.utils.message_templates import DATA_UNSHARED_EXPLANATION
from user.models import UserActionLogEntry 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, Abstract compensation model which holds basic attributes, shared by subclasses like the regular Compensation,
EMA or EcoAccount. EMA or EcoAccount.
@ -41,8 +44,6 @@ class AbstractCompensation(BaseObject):
deadlines = models.ManyToManyField("konova.Deadline", blank=True, related_name="+") deadlines = models.ManyToManyField("konova.Deadline", blank=True, related_name="+")
geometry = models.ForeignKey(Geometry, null=True, blank=True, on_delete=models.SET_NULL)
class Meta: class Meta:
abstract = True abstract = True
@ -156,6 +157,22 @@ class AbstractCompensation(BaseObject):
checker.run_check() checker.run_check()
return checker 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): class CEFMixin(models.Model):
""" Provides CEF flag as Mixin """ 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 sum_after_states = after_states.aggregate(Sum("surface"))["surface__sum"] or 0
diff_states = abs(sum_before_states - sum_after_states) diff_states = abs(sum_before_states - sum_after_states)
if not is_data_shared: request = comp.set_status_messages(request)
messages.info(request, DATA_UNSHARED_EXPLANATION)
context = { context = {
"obj": comp, "obj": comp,

View File

@ -202,8 +202,7 @@ def detail_view(request: HttpRequest, id: str):
) )
actions = acc.actions.all() actions = acc.actions.all()
if not is_data_shared: request = acc.set_status_messages(request)
messages.info(request, DATA_UNSHARED_EXPLANATION)
context = { context = {
"obj": acc, "obj": acc,

View File

@ -7,15 +7,17 @@ Created on: 15.11.21
""" """
import shutil import shutil
from django.contrib.auth.models import User from django.contrib import messages
from django.db import models from django.db import models
from django.db.models import QuerySet from django.db.models import QuerySet
from django.http import HttpRequest
from compensation.models import AbstractCompensation from compensation.models import AbstractCompensation
from ema.managers import EmaManager from ema.managers import EmaManager
from ema.utils.quality import EmaQualityChecker from ema.utils.quality import EmaQualityChecker
from konova.models import AbstractDocument, generate_document_file_upload_path, RecordableObjectMixin, ShareableObjectMixin from konova.models import AbstractDocument, generate_document_file_upload_path, RecordableObjectMixin, ShareableObjectMixin
from konova.settings import DEFAULT_SRID_RLP, LANIS_LINK_TEMPLATE from konova.settings import DEFAULT_SRID_RLP, LANIS_LINK_TEMPLATE
from konova.utils.message_templates import DATA_UNSHARED_EXPLANATION
class Ema(AbstractCompensation, ShareableObjectMixin, RecordableObjectMixin): class Ema(AbstractCompensation, ShareableObjectMixin, RecordableObjectMixin):
@ -91,6 +93,22 @@ class Ema(AbstractCompensation, ShareableObjectMixin, RecordableObjectMixin):
) )
return docs 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): 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 sum_after_states = after_states.aggregate(Sum("surface"))["surface__sum"] or 0
diff_states = abs(sum_before_states - sum_after_states) diff_states = abs(sum_before_states - sum_after_states)
if not is_data_shared: ema.set_status_messages(request)
messages.info(request, DATA_UNSHARED_EXPLANATION)
context = { context = {
"obj": ema, "obj": ema,

View File

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

View File

@ -7,6 +7,7 @@ Created on: 15.11.21
""" """
import shutil import shutil
from django.contrib import messages
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db import models, transaction from django.db import models, transaction
from django.db.models import QuerySet 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.responsibility import Responsibility
from intervention.models.revocation import RevocationDocument, Revocation from intervention.models.revocation import RevocationDocument, Revocation
from intervention.utils.quality import InterventionQualityChecker from intervention.utils.quality import InterventionQualityChecker
from konova.models import generate_document_file_upload_path, AbstractDocument, Geometry, BaseObject, ShareableObjectMixin, \ from konova.models import generate_document_file_upload_path, AbstractDocument, BaseObject, \
RecordableObjectMixin, CheckableObjectMixin ShareableObjectMixin, \
RecordableObjectMixin, CheckableObjectMixin, GeoReferencedMixin
from konova.settings import LANIS_LINK_TEMPLATE, LANIS_ZOOM_LUT, DEFAULT_SRID_RLP 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 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. Interventions are e.g. construction sites where nature used to be.
""" """
@ -42,7 +45,6 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec
blank=True, blank=True,
help_text="Holds data on legal dates or law" help_text="Holds data on legal dates or law"
) )
geometry = models.ForeignKey(Geometry, null=True, blank=True, on_delete=models.SET_NULL)
objects = InterventionManager() objects = InterventionManager()
@ -263,6 +265,22 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec
if self.checked: if self.checked:
self.set_unchecked() 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): class InterventionDocument(AbstractDocument):
""" """

View File

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

View File

@ -7,7 +7,7 @@ Created on: 22.07.21
""" """
from django.contrib import admin from django.contrib import admin
from konova.models import Geometry, Deadline from konova.models import Geometry, Deadline, GeometryConflict
class GeometryAdmin(admin.ModelAdmin): 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): class AbstractDocumentAdmin(admin.ModelAdmin):
list_display = [ list_display = [
"id", "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(Geometry, GeometryAdmin)
admin.site.register(GeometryConflict, GeometryConflictAdmin)
admin.site.register(Deadline, DeadlineAdmin) admin.site.register(Deadline, DeadlineAdmin)

View File

@ -282,11 +282,13 @@ class SimpleGeomForm(BaseForm):
""" """
try: try:
if self.instance is None or self.instance.geometry is None:
raise LookupError
geometry = self.instance.geometry geometry = self.instance.geometry
geometry.geom = self.cleaned_data.get("geom", MultiPolygon(srid=DEFAULT_SRID)) geometry.geom = self.cleaned_data.get("geom", MultiPolygon(srid=DEFAULT_SRID))
geometry.modified = action geometry.modified = action
geometry.save() geometry.save()
except AttributeError: except LookupError:
# No geometry or linked instance holding a geometry exist --> create a new one! # No geometry or linked instance holding a geometry exist --> create a new one!
geometry = Geometry.objects.create( geometry = Geometry.objects.create(
geom=self.cleaned_data.get("geom", MultiPolygon(srid=DEFAULT_SRID)), geom=self.cleaned_data.get("geom", MultiPolygon(srid=DEFAULT_SRID)),

View File

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

View File

@ -6,13 +6,110 @@ Created on: 15.11.21
""" """
from django.contrib.gis.db.models import MultiPolygonField 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): 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 from konova.settings import DEFAULT_SRID
geom = MultiPolygonField(null=True, blank=True, srid=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 import uuid
from abc import abstractmethod
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.models import User 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 intervention.settings import INTERVENTION_IDENTIFIER_LENGTH, INTERVENTION_IDENTIFIER_TEMPLATE
from konova.utils import generators from konova.utils import generators
from konova.utils.generators import generate_random_string 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 from user.models import UserActionLogEntry, UserAction
@ -94,6 +95,10 @@ class BaseObject(BaseResource):
class Meta: class Meta:
abstract = True abstract = True
@abstractmethod
def set_status_messages(self, request: HttpRequest):
raise NotImplementedError
def mark_as_deleted(self, user: User): def mark_as_deleted(self, user: User):
""" Mark an entry as deleted """ Mark an entry as deleted
@ -407,3 +412,29 @@ class ShareableObjectMixin(models.Model):
id__in=accessing_users id__in=accessing_users
) )
self.share_with_list(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_COMPENSATION_STATE = _("Added compensation state")
ADDED_DEADLINE = _("Added deadline") ADDED_DEADLINE = _("Added deadline")
ADDED_COMPENSATION_ACTION = _("Added compensation action") 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:52 intervention/forms/forms.py:154
#: intervention/forms/forms.py:166 intervention/forms/modalForms.py:125 #: intervention/forms/forms.py:166 intervention/forms/modalForms.py:125
#: intervention/forms/modalForms.py:138 intervention/forms/modalForms.py:151 #: 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:139 konova/forms.py:240 konova/forms.py:308
#: konova/forms.py:333 konova/forms.py:343 konova/forms.py:356 #: konova/forms.py:335 konova/forms.py:345 konova/forms.py:358
#: konova/forms.py:368 konova/forms.py:386 user/forms.py:38 #: konova/forms.py:370 konova/forms.py:388 user/forms.py:38
#, fuzzy #, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \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" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\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/includes/documents.html:28
#: intervention/templates/intervention/detail/view.html:31 #: intervention/templates/intervention/detail/view.html:31
#: intervention/templates/intervention/report/report.html:12 #: intervention/templates/intervention/report/report.html:12
#: konova/forms.py:332 #: konova/forms.py:334
msgid "Title" msgid "Title"
msgstr "Bezeichnung" msgstr "Bezeichnung"
@ -356,7 +356,7 @@ msgstr "Kompensation XY; Flur ABC"
#: intervention/templates/intervention/detail/includes/documents.html:31 #: intervention/templates/intervention/detail/includes/documents.html:31
#: intervention/templates/intervention/detail/includes/payments.html:34 #: intervention/templates/intervention/detail/includes/payments.html:34
#: intervention/templates/intervention/detail/includes/revocation.html:38 #: 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" msgid "Comment"
msgstr "Kommentar" 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:62 compensation/forms/modalForms.py:239
#: compensation/forms/modalForms.py:317 intervention/forms/modalForms.py:152 #: compensation/forms/modalForms.py:317 intervention/forms/modalForms.py:152
#: konova/forms.py:369 #: konova/forms.py:371
msgid "Additional comment, maximum {} letters" msgid "Additional comment, maximum {} letters"
msgstr "Zusätzlicher Kommentar, maximal {} Zeichen" msgstr "Zusätzlicher Kommentar, maximal {} Zeichen"
@ -614,7 +614,7 @@ msgstr ""
msgid "Pieces" msgid "Pieces"
msgstr "Stück" 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" msgid "Added deadline"
msgstr "Frist/Termin hinzugefügt" msgstr "Frist/Termin hinzugefügt"
@ -793,7 +793,7 @@ msgstr "Dokumente"
#: compensation/templates/compensation/detail/eco_account/includes/documents.html:14 #: compensation/templates/compensation/detail/eco_account/includes/documents.html:14
#: ema/templates/ema/detail/includes/documents.html:14 #: ema/templates/ema/detail/includes/documents.html:14
#: intervention/templates/intervention/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" msgid "Add new document"
msgstr "Neues Dokument hinzufügen" msgstr "Neues Dokument hinzufügen"
@ -1056,42 +1056,42 @@ msgstr "Kompensation {} hinzugefügt"
msgid "Compensation {} edited" msgid "Compensation {} edited"
msgstr "Kompensation {} bearbeitet" msgstr "Kompensation {} bearbeitet"
#: compensation/views/compensation.py:229 compensation/views/eco_account.py:308 #: compensation/views/compensation.py:228 compensation/views/eco_account.py:307
#: ema/views.py:182 intervention/views.py:475 #: ema/views.py:181 intervention/views.py:474
msgid "Log" msgid "Log"
msgstr "Log" msgstr "Log"
#: compensation/views/compensation.py:252 #: compensation/views/compensation.py:251
msgid "Compensation removed" msgid "Compensation removed"
msgstr "Kompensation entfernt" msgstr "Kompensation entfernt"
#: compensation/views/compensation.py:273 compensation/views/eco_account.py:460 #: compensation/views/compensation.py:272 compensation/views/eco_account.py:459
#: ema/views.py:349 intervention/views.py:129 #: ema/views.py:348 intervention/views.py:129
msgid "Document added" msgid "Document added"
msgstr "Dokument hinzugefügt" msgstr "Dokument hinzugefügt"
#: compensation/views/compensation.py:342 compensation/views/eco_account.py:354 #: compensation/views/compensation.py:341 compensation/views/eco_account.py:353
#: ema/views.py:287 #: ema/views.py:286
msgid "State added" msgid "State added"
msgstr "Zustand hinzugefügt" msgstr "Zustand hinzugefügt"
#: compensation/views/compensation.py:363 compensation/views/eco_account.py:375 #: compensation/views/compensation.py:362 compensation/views/eco_account.py:374
#: ema/views.py:308 #: ema/views.py:307
msgid "Action added" msgid "Action added"
msgstr "Maßnahme hinzugefügt" msgstr "Maßnahme hinzugefügt"
#: compensation/views/compensation.py:384 compensation/views/eco_account.py:440 #: compensation/views/compensation.py:383 compensation/views/eco_account.py:439
#: ema/views.py:329 #: ema/views.py:328
msgid "Deadline added" msgid "Deadline added"
msgstr "Frist/Termin hinzugefügt" msgstr "Frist/Termin hinzugefügt"
#: compensation/views/compensation.py:406 compensation/views/eco_account.py:397 #: compensation/views/compensation.py:405 compensation/views/eco_account.py:396
#: ema/views.py:419 #: ema/views.py:418
msgid "State removed" msgid "State removed"
msgstr "Zustand gelöscht" msgstr "Zustand gelöscht"
#: compensation/views/compensation.py:428 compensation/views/eco_account.py:419 #: compensation/views/compensation.py:427 compensation/views/eco_account.py:418
#: ema/views.py:441 #: ema/views.py:440
msgid "Action removed" msgid "Action removed"
msgstr "Maßnahme entfernt" msgstr "Maßnahme entfernt"
@ -1103,45 +1103,45 @@ msgstr "Ökokonto {} hinzugefügt"
msgid "Eco-Account {} edited" msgid "Eco-Account {} edited"
msgstr "Ökokonto {} bearbeitet" msgstr "Ökokonto {} bearbeitet"
#: compensation/views/eco_account.py:256 #: compensation/views/eco_account.py:255
msgid "Eco-account removed" msgid "Eco-account removed"
msgstr "Ökokonto entfernt" msgstr "Ökokonto entfernt"
#: compensation/views/eco_account.py:284 #: compensation/views/eco_account.py:283
msgid "Deduction removed" msgid "Deduction removed"
msgstr "Abbuchung entfernt" msgstr "Abbuchung entfernt"
#: compensation/views/eco_account.py:329 ema/views.py:262 #: compensation/views/eco_account.py:328 ema/views.py:261
#: intervention/views.py:517 #: intervention/views.py:516
msgid "{} unrecorded" msgid "{} unrecorded"
msgstr "{} entzeichnet" msgstr "{} entzeichnet"
#: compensation/views/eco_account.py:329 ema/views.py:262 #: compensation/views/eco_account.py:328 ema/views.py:261
#: intervention/views.py:517 #: intervention/views.py:516
msgid "{} recorded" msgid "{} recorded"
msgstr "{} verzeichnet" 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" msgid "Deduction added"
msgstr "Abbuchung hinzugefügt" msgstr "Abbuchung hinzugefügt"
#: compensation/views/eco_account.py:613 ema/views.py:517 #: compensation/views/eco_account.py:612 ema/views.py:516
#: intervention/views.py:373 #: intervention/views.py:372
msgid "{} has already been shared with you" msgid "{} has already been shared with you"
msgstr "{} wurde bereits für Sie freigegeben" msgstr "{} wurde bereits für Sie freigegeben"
#: compensation/views/eco_account.py:618 ema/views.py:522 #: compensation/views/eco_account.py:617 ema/views.py:521
#: intervention/views.py:378 #: intervention/views.py:377
msgid "{} has been shared with you" msgid "{} has been shared with you"
msgstr "{} ist nun für Sie freigegeben" msgstr "{} ist nun für Sie freigegeben"
#: compensation/views/eco_account.py:625 ema/views.py:529 #: compensation/views/eco_account.py:624 ema/views.py:528
#: intervention/views.py:385 #: intervention/views.py:384
msgid "Share link invalid" msgid "Share link invalid"
msgstr "Freigabelink ungültig" msgstr "Freigabelink ungültig"
#: compensation/views/eco_account.py:648 ema/views.py:552 #: compensation/views/eco_account.py:647 ema/views.py:551
#: intervention/views.py:408 #: intervention/views.py:407
msgid "Share settings updated" msgid "Share settings updated"
msgstr "Freigabe Einstellungen aktualisiert" msgstr "Freigabe Einstellungen aktualisiert"
@ -1185,11 +1185,11 @@ msgstr "Ersatzzahlungsmaßnahme"
msgid "EMA {} added" msgid "EMA {} added"
msgstr "EMA {} hinzugefügt" msgstr "EMA {} hinzugefügt"
#: ema/views.py:211 #: ema/views.py:210
msgid "EMA {} edited" msgid "EMA {} edited"
msgstr "EMA {} bearbeitet" msgstr "EMA {} bearbeitet"
#: ema/views.py:243 #: ema/views.py:242
msgid "EMA removed" msgid "EMA removed"
msgstr "EMA entfernt" msgstr "EMA entfernt"
@ -1333,7 +1333,7 @@ msgstr "Kompensationen und Zahlungen geprüft"
msgid "Run check" msgid "Run check"
msgstr "Prüfung vornehmen" msgstr "Prüfung vornehmen"
#: intervention/forms/modalForms.py:196 konova/forms.py:451 #: intervention/forms/modalForms.py:196 konova/forms.py:453
msgid "" msgid ""
"I, {} {}, confirm that all necessary control steps have been performed by " "I, {} {}, confirm that all necessary control steps have been performed by "
"myself." "myself."
@ -1476,27 +1476,27 @@ msgstr "Eingriff {} hinzugefügt"
msgid "This intervention has {} revocations" msgid "This intervention has {} revocations"
msgstr "Dem Eingriff liegen {} Widersprüche vor" msgstr "Dem Eingriff liegen {} Widersprüche vor"
#: intervention/views.py:291 #: intervention/views.py:290
msgid "Intervention {} edited" msgid "Intervention {} edited"
msgstr "Eingriff {} bearbeitet" msgstr "Eingriff {} bearbeitet"
#: intervention/views.py:326 #: intervention/views.py:325
msgid "{} removed" msgid "{} removed"
msgstr "{} entfernt" msgstr "{} entfernt"
#: intervention/views.py:347 #: intervention/views.py:346
msgid "Revocation removed" msgid "Revocation removed"
msgstr "Widerspruch entfernt" msgstr "Widerspruch entfernt"
#: intervention/views.py:429 #: intervention/views.py:428
msgid "Check performed" msgid "Check performed"
msgstr "Prüfung durchgeführt" msgstr "Prüfung durchgeführt"
#: intervention/views.py:451 #: intervention/views.py:450
msgid "Revocation added" msgid "Revocation added"
msgstr "Widerspruch hinzugefügt" msgstr "Widerspruch hinzugefügt"
#: intervention/views.py:522 #: intervention/views.py:521
msgid "There are errors on this intervention:" msgid "There are errors on this intervention:"
msgstr "Es liegen Fehler in diesem Eingriff vor:" msgstr "Es liegen Fehler in diesem Eingriff vor:"
@ -1525,11 +1525,11 @@ msgstr "Speichern"
msgid "Not editable" msgid "Not editable"
msgstr "Nicht editierbar" msgstr "Nicht editierbar"
#: konova/forms.py:138 konova/forms.py:305 #: konova/forms.py:138 konova/forms.py:307
msgid "Confirm" msgid "Confirm"
msgstr "Bestätige" msgstr "Bestätige"
#: konova/forms.py:150 konova/forms.py:314 #: konova/forms.py:150 konova/forms.py:316
msgid "Remove" msgid "Remove"
msgstr "Löschen" msgstr "Löschen"
@ -1542,56 +1542,56 @@ msgstr "Sie sind dabei {} {} zu löschen"
msgid "Geometry" msgid "Geometry"
msgstr "Geometrie" msgstr "Geometrie"
#: konova/forms.py:315 #: konova/forms.py:317
msgid "Are you sure?" msgid "Are you sure?"
msgstr "Sind Sie sicher?" msgstr "Sind Sie sicher?"
#: konova/forms.py:342 #: konova/forms.py:344
msgid "Created on" msgid "Created on"
msgstr "Erstellt" msgstr "Erstellt"
#: konova/forms.py:344 #: konova/forms.py:346
msgid "When has this file been created? Important for photos." msgid "When has this file been created? Important for photos."
msgstr "Wann wurde diese Datei erstellt oder das Foto aufgenommen?" 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 #: venv/lib/python3.7/site-packages/django/db/models/fields/files.py:231
msgid "File" msgid "File"
msgstr "Datei" msgstr "Datei"
#: konova/forms.py:357 #: konova/forms.py:359
msgid "Allowed formats: pdf, jpg, png. Max size 15 MB." msgid "Allowed formats: pdf, jpg, png. Max size 15 MB."
msgstr "Formate: pdf, jpg, png. Maximal 15 MB." msgstr "Formate: pdf, jpg, png. Maximal 15 MB."
#: konova/forms.py:403 #: konova/forms.py:405
msgid "Unsupported file type" msgid "Unsupported file type"
msgstr "Dateiformat nicht unterstützt" msgstr "Dateiformat nicht unterstützt"
#: konova/forms.py:410 #: konova/forms.py:412
msgid "File too large" msgid "File too large"
msgstr "Datei zu groß" msgstr "Datei zu groß"
#: konova/forms.py:419 #: konova/forms.py:421
msgid "Added document" msgid "Added document"
msgstr "Dokument hinzugefügt" msgstr "Dokument hinzugefügt"
#: konova/forms.py:442 #: konova/forms.py:444
msgid "Confirm record" msgid "Confirm record"
msgstr "Verzeichnen bestätigen" msgstr "Verzeichnen bestätigen"
#: konova/forms.py:450 #: konova/forms.py:452
msgid "Record data" msgid "Record data"
msgstr "Daten verzeichnen" msgstr "Daten verzeichnen"
#: konova/forms.py:457 #: konova/forms.py:459
msgid "Confirm unrecord" msgid "Confirm unrecord"
msgstr "Entzeichnen bestätigen" msgstr "Entzeichnen bestätigen"
#: konova/forms.py:458 #: konova/forms.py:460
msgid "Unrecord data" msgid "Unrecord data"
msgstr "Daten entzeichnen" msgstr "Daten entzeichnen"
#: konova/forms.py:459 #: konova/forms.py:461
msgid "I, {} {}, confirm that this data must be unrecorded." msgid "I, {} {}, confirm that this data must be unrecorded."
msgstr "" msgstr ""
"Ich, {} {}, bestätige, dass diese Daten wieder entzeichnet werden müssen." "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 " "Action canceled. Eco account is recorded or deductions exist. Only "
"conservation office member can perform this action." "conservation office member can perform this action."
msgstr "" 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 #: konova/utils/message_templates.py:25
msgid "Edited general data" msgid "Edited general data"
@ -1739,6 +1741,10 @@ msgstr "Zustand hinzugefügt"
msgid "Added compensation action" msgid "Added compensation action"
msgstr "Maßnahme hinzufügen" 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 #: konova/utils/messenger.py:69
msgid "{} checked" msgid "{} checked"
msgstr "{} geprüft" msgstr "{} geprüft"