#138 WIP Improvements

* adds geom back writing to form field in case of invalid geometry, so the invalid geometry will be shown again
* updates tests
* fixes bug where race condition of celery workers could lead to duplicates in parcels (needs migration)
This commit is contained in:
mpeltriaux 2022-04-20 11:55:03 +02:00
parent eeccba3968
commit 8d34580090
12 changed files with 179 additions and 46 deletions

View File

@ -50,10 +50,11 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase):
test_id = self.create_dummy_string() test_id = self.create_dummy_string()
test_title = self.create_dummy_string() test_title = self.create_dummy_string()
test_geom = self.create_dummy_geometry() test_geom = self.create_dummy_geometry()
geom_json = self.create_geojson(test_geom)
post_data = { post_data = {
"identifier": test_id, "identifier": test_id,
"title": test_title, "title": test_title,
"geom": test_geom.geojson, "geom": geom_json,
"intervention": self.intervention.id, "intervention": self.intervention.id,
} }
pre_creation_intervention_log_count = self.intervention.log.count() pre_creation_intervention_log_count = self.intervention.log.count()
@ -88,10 +89,11 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase):
test_id = self.create_dummy_string() test_id = self.create_dummy_string()
test_title = self.create_dummy_string() test_title = self.create_dummy_string()
test_geom = self.create_dummy_geometry() test_geom = self.create_dummy_geometry()
geom_json = self.create_geojson(test_geom)
post_data = { post_data = {
"identifier": test_id, "identifier": test_id,
"title": test_title, "title": test_title,
"geom": test_geom.geojson, "geom": geom_json,
} }
pre_creation_intervention_log_count = self.intervention.log.count() pre_creation_intervention_log_count = self.intervention.log.count()

View File

@ -40,12 +40,13 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase):
test_id = self.create_dummy_string() test_id = self.create_dummy_string()
test_title = self.create_dummy_string() test_title = self.create_dummy_string()
test_geom = self.create_dummy_geometry() test_geom = self.create_dummy_geometry()
geom_json = self.create_geojson(test_geom)
test_deductable_surface = 1000 test_deductable_surface = 1000
test_conservation_office = self.get_conservation_office_code() test_conservation_office = self.get_conservation_office_code()
post_data = { post_data = {
"identifier": test_id, "identifier": test_id,
"title": test_title, "title": test_title,
"geom": test_geom.geojson, "geom": geom_json,
"deductable_surface": test_deductable_surface, "deductable_surface": test_deductable_surface,
"conservation_office": test_conservation_office.id "conservation_office": test_conservation_office.id
} }

View File

@ -41,11 +41,12 @@ class EmaWorkflowTestCase(BaseWorkflowTestCase):
test_id = self.create_dummy_string() test_id = self.create_dummy_string()
test_title = self.create_dummy_string() test_title = self.create_dummy_string()
test_geom = self.create_dummy_geometry() test_geom = self.create_dummy_geometry()
geom_json = self.create_geojson(test_geom)
test_conservation_office = self.get_conservation_office_code() test_conservation_office = self.get_conservation_office_code()
post_data = { post_data = {
"identifier": test_id, "identifier": test_id,
"title": test_title, "title": test_title,
"geom": test_geom.geojson, "geom": geom_json,
"conservation_office": test_conservation_office.id "conservation_office": test_conservation_office.id
} }
self.client_user.post(new_url, post_data) self.client_user.post(new_url, post_data)

View File

@ -46,6 +46,7 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase):
test_id = self.create_dummy_string() test_id = self.create_dummy_string()
test_title = self.create_dummy_string() test_title = self.create_dummy_string()
test_geom = self.create_dummy_geometry() test_geom = self.create_dummy_geometry()
geom_json = self.create_geojson(test_geom)
new_url = reverse("intervention:new", args=()) new_url = reverse("intervention:new", args=())
@ -59,7 +60,7 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase):
post_data = { post_data = {
"identifier": test_id, "identifier": test_id,
"title": test_title, "title": test_title,
"geom": test_geom.geojson, "geom": geom_json,
} }
response = self.client_user.post( response = self.client_user.post(
new_url, new_url,

View File

@ -293,26 +293,33 @@ class SimpleGeomForm(BaseForm):
geom = self.instance.geometry.geom geom = self.instance.geometry.geom
geom.transform(ct=DEFAULT_SRID_RLP) geom.transform(ct=DEFAULT_SRID_RLP)
self.empty = geom.empty self.empty = geom.empty
if self.empty:
raise AttributeError
geom = geom.geojson geom = geom.geojson
except AttributeError: except AttributeError:
# If no geometry exists for this form, we simply set the value to None and zoom to the maximum level # If no geometry exists for this form, we simply set the value to None and zoom to the maximum level
geom = "" geom = ""
self.empty = True self.empty = True
self.fields["geom"].widget.attrs["default_zoom"] = 1
self.initialize_form_field("geom", geom) self.initialize_form_field("geom", geom)
def is_valid(self): def is_valid(self):
super_valid = super().is_valid() super().is_valid()
is_valid = True is_valid = True
# Get geojson from form # Get geojson from form
geom = self.data["geom"] geom = self.data["geom"]
geom = json.loads(geom) geom = json.loads(geom)
# Write submitted data back into form field to make sure invalid geometry
# will be rendered again on failed submit
self.initialize_form_field("geom", self.data["geom"])
# Read geojson into gdal geometry # Read geojson into gdal geometry
# HINT: This can be simplified if the geojson format holds data in epsg:4326 (GDAL provides direct creation for
# this case)
features = [] features = []
features_json = geom["features"] features_json = geom.get("features", [])
for feature in features_json: for feature in features_json:
g = gdal.OGRGeometry(json.dumps(feature["geometry"]), srs=DEFAULT_SRID_RLP) g = gdal.OGRGeometry(json.dumps(feature["geometry"]), srs=DEFAULT_SRID_RLP)
if g.geom_type not in ["Polygon", "MultiPolygon"]: if g.geom_type not in ["Polygon", "MultiPolygon"]:
@ -327,9 +334,12 @@ class SimpleGeomForm(BaseForm):
form_geom.transform(coord_trans=DEFAULT_SRID) form_geom.transform(coord_trans=DEFAULT_SRID)
if form_geom.geom_type != "MultiPolygon": if form_geom.geom_type != "MultiPolygon":
form_geom = MultiPolygon(MultiPolygon.from_ewkt(form_geom.ewkt)) form_geom = MultiPolygon(MultiPolygon.from_ewkt(form_geom.ewkt))
self.cleaned_data = {
"geom": form_geom.wkt # Write unioned Multipolygon into cleaned data
} if self.cleaned_data is None:
self.cleaned_data = {}
self.cleaned_data["geom"] = form_geom.wkt
return is_valid return is_valid
def save(self, action: UserActionLogEntry): def save(self, action: UserActionLogEntry):

View File

@ -0,0 +1,17 @@
# Generated by Django 3.1.3 on 2022-04-20 08:34
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('konova', '0009_auto_20220411_1004'),
]
operations = [
migrations.AddConstraint(
model_name='parcel',
constraint=models.UniqueConstraint(fields=('district', 'municipal', 'parcel_group', 'flr', 'flrstck_nnr', 'flrstck_zhlr'), name='Unique parcel constraint'),
),
]

View File

@ -0,0 +1,25 @@
# Generated by Django 3.1.3 on 2022-04-20 09:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('konova', '0010_auto_20220420_1034'),
]
operations = [
migrations.AddConstraint(
model_name='district',
constraint=models.UniqueConstraint(fields=('key', 'name'), name='Unique district constraint'),
),
migrations.AddConstraint(
model_name='municipal',
constraint=models.UniqueConstraint(fields=('key', 'name', 'district'), name='Unique municipal constraint'),
),
migrations.AddConstraint(
model_name='parcelgroup',
constraint=models.UniqueConstraint(fields=('key', 'name', 'municipal'), name='Unique parcel group constraint'),
),
]

View File

@ -6,7 +6,7 @@ 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 import models, transaction
from django.utils import timezone from django.utils import timezone
from konova.models import BaseResource, UuidModel from konova.models import BaseResource, UuidModel
@ -96,6 +96,7 @@ class Geometry(BaseResource):
objs += set_objs objs += set_objs
return objs return objs
@transaction.atomic
def update_parcels(self): def update_parcels(self):
""" Updates underlying parcel information """ Updates underlying parcel information

View File

@ -39,7 +39,17 @@ class District(UuidModel, AdministrativeSpatialReference):
""" The model District refers to "Kreis" """ The model District refers to "Kreis"
""" """
pass
class Meta:
constraints = [
models.UniqueConstraint(
fields=[
"key",
"name",
],
name="Unique district constraint"
)
]
class Municipal(UuidModel, AdministrativeSpatialReference): class Municipal(UuidModel, AdministrativeSpatialReference):
@ -53,6 +63,18 @@ class Municipal(UuidModel, AdministrativeSpatialReference):
blank=True, blank=True,
) )
class Meta:
constraints = [
models.UniqueConstraint(
fields=[
"key",
"name",
"district",
],
name="Unique municipal constraint"
)
]
class ParcelGroup(UuidModel, AdministrativeSpatialReference): class ParcelGroup(UuidModel, AdministrativeSpatialReference):
""" The model ParcelGroup refers to "Gemarkung", which is defined as a loose group of parcels """ The model ParcelGroup refers to "Gemarkung", which is defined as a loose group of parcels
@ -65,6 +87,18 @@ class ParcelGroup(UuidModel, AdministrativeSpatialReference):
blank=True, blank=True,
) )
class Meta:
constraints = [
models.UniqueConstraint(
fields=[
"key",
"name",
"municipal",
],
name="Unique parcel group constraint"
)
]
class Parcel(UuidModel): class Parcel(UuidModel):
""" The Parcel model holds administrative data on covered properties. """ The Parcel model holds administrative data on covered properties.
@ -106,6 +140,21 @@ class Parcel(UuidModel):
) )
updated_on = models.DateTimeField(auto_now_add=True) updated_on = models.DateTimeField(auto_now_add=True)
class Meta:
constraints = [
models.UniqueConstraint(
fields=[
"district",
"municipal",
"parcel_group",
"flr",
"flrstck_nnr",
"flrstck_zhlr",
],
name="Unique parcel constraint"
)
]
def __str__(self): def __str__(self):
return f"{self.parcel_group} | {self.flr} | {self.flrstck_zhlr} | {self.flrstck_nnr}" return f"{self.parcel_group} | {self.flr} | {self.flrstck_zhlr} | {self.flrstck_nnr}"

View File

@ -6,9 +6,11 @@ Created on: 26.10.21
""" """
import datetime import datetime
import json
from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID
from ema.models import Ema from ema.models import Ema
from konova.sub_settings.lanis_settings import DEFAULT_SRID_RLP
from user.models import User, Team from user.models import User, Team
from django.contrib.auth.models import Group from django.contrib.auth.models import Group
from django.contrib.gis.geos import MultiPolygon, Polygon from django.contrib.gis.geos import MultiPolygon, Polygon
@ -287,8 +289,28 @@ class BaseTestCase(TestCase):
""" """
polygon = Polygon.from_bbox((7.592449, 50.359385, 7.593382, 50.359874)) polygon = Polygon.from_bbox((7.592449, 50.359385, 7.593382, 50.359874))
polygon.srid = 4326 polygon.srid = 4326
polygon = polygon.transform(3857, clone=True) polygon = polygon.transform(DEFAULT_SRID_RLP, clone=True)
return MultiPolygon(polygon, srid=3857) # 3857 is the default srid used for MultiPolygonField in the form return MultiPolygon(polygon, srid=DEFAULT_SRID_RLP)
def create_geojson(self, geometry):
""" Creates a default structure including geojson from a geometry
Args:
geometry ():
Returns:
"""
geom_json = {
"features": [
{
"type": "Feature",
"geometry": json.loads(geometry.geojson),
}
]
}
geom_json = json.dumps(geom_json)
return geom_json
def create_dummy_handler(self) -> Handler: def create_dummy_handler(self) -> Handler:
""" Creates a Handler """ Creates a Handler

Binary file not shown.

View File

@ -18,15 +18,15 @@
#: konova/filters/mixins.py:277 konova/filters/mixins.py:323 #: konova/filters/mixins.py:277 konova/filters/mixins.py:323
#: konova/filters/mixins.py:361 konova/filters/mixins.py:362 #: konova/filters/mixins.py:361 konova/filters/mixins.py:362
#: konova/filters/mixins.py:393 konova/filters/mixins.py:394 #: konova/filters/mixins.py:393 konova/filters/mixins.py:394
#: konova/forms.py:177 konova/forms.py:278 konova/forms.py:349 #: konova/forms.py:179 konova/forms.py:281 konova/forms.py:382
#: konova/forms.py:393 konova/forms.py:403 konova/forms.py:416 #: konova/forms.py:426 konova/forms.py:436 konova/forms.py:449
#: konova/forms.py:428 konova/forms.py:446 user/forms.py:42 #: konova/forms.py:461 konova/forms.py:479 user/forms.py:42
#, 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: 2022-04-19 13:28+0200\n" "POT-Creation-Date: 2022-04-20 09:51+0200\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"
@ -77,7 +77,7 @@ msgstr "Bericht generieren"
msgid "Select a timespan and the desired conservation office" msgid "Select a timespan and the desired conservation office"
msgstr "Wählen Sie die Zeitspanne und die gewünschte Eintragungsstelle" msgstr "Wählen Sie die Zeitspanne und die gewünschte Eintragungsstelle"
#: analysis/forms.py:69 konova/forms.py:225 #: analysis/forms.py:69 konova/forms.py:227
msgid "Continue" msgid "Continue"
msgstr "Weiter" msgstr "Weiter"
@ -342,7 +342,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:392 #: konova/forms.py:425
msgid "Title" msgid "Title"
msgstr "Bezeichnung" msgstr "Bezeichnung"
@ -369,7 +369,7 @@ msgstr "Kompensation XY; Flur ABC"
#: intervention/templates/intervention/detail/includes/documents.html:34 #: intervention/templates/intervention/detail/includes/documents.html:34
#: 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:427 konova/templates/konova/includes/comment_card.html:16 #: konova/forms.py:460 konova/templates/konova/includes/comment_card.html:16
msgid "Comment" msgid "Comment"
msgstr "Kommentar" msgstr "Kommentar"
@ -493,7 +493,7 @@ msgid "Due on which date"
msgstr "Zahlung wird an diesem Datum erwartet" msgstr "Zahlung wird an diesem Datum erwartet"
#: compensation/forms/modalForms.py:64 compensation/forms/modalForms.py:359 #: compensation/forms/modalForms.py:64 compensation/forms/modalForms.py:359
#: intervention/forms/modalForms.py:177 konova/forms.py:429 #: intervention/forms/modalForms.py:177 konova/forms.py:462
msgid "Additional comment, maximum {} letters" msgid "Additional comment, maximum {} letters"
msgstr "Zusätzlicher Kommentar, maximal {} Zeichen" msgstr "Zusätzlicher Kommentar, maximal {} Zeichen"
@ -538,7 +538,7 @@ msgstr "Neuer Zustand"
msgid "Insert data for the new state" msgid "Insert data for the new state"
msgstr "Geben Sie die Daten des neuen Zustandes ein" msgstr "Geben Sie die Daten des neuen Zustandes ein"
#: compensation/forms/modalForms.py:217 konova/forms.py:227 #: compensation/forms/modalForms.py:217 konova/forms.py:229
msgid "Object removed" msgid "Object removed"
msgstr "Objekt entfernt" msgstr "Objekt entfernt"
@ -871,7 +871,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:445 #: konova/forms.py:478
msgid "Add new document" msgid "Add new document"
msgstr "Neues Dokument hinzufügen" msgstr "Neues Dokument hinzufügen"
@ -879,7 +879,7 @@ msgstr "Neues Dokument hinzufügen"
#: compensation/templates/compensation/detail/eco_account/includes/documents.html:31 #: compensation/templates/compensation/detail/eco_account/includes/documents.html:31
#: ema/templates/ema/detail/includes/documents.html:31 #: ema/templates/ema/detail/includes/documents.html:31
#: intervention/templates/intervention/detail/includes/documents.html:31 #: intervention/templates/intervention/detail/includes/documents.html:31
#: konova/forms.py:402 #: konova/forms.py:435
msgid "Created on" msgid "Created on"
msgstr "Erstellt" msgstr "Erstellt"
@ -887,7 +887,7 @@ msgstr "Erstellt"
#: compensation/templates/compensation/detail/eco_account/includes/documents.html:61 #: compensation/templates/compensation/detail/eco_account/includes/documents.html:61
#: ema/templates/ema/detail/includes/documents.html:61 #: ema/templates/ema/detail/includes/documents.html:61
#: intervention/templates/intervention/detail/includes/documents.html:65 #: intervention/templates/intervention/detail/includes/documents.html:65
#: konova/forms.py:507 #: konova/forms.py:540
msgid "Edit document" msgid "Edit document"
msgstr "Dokument bearbeiten" msgstr "Dokument bearbeiten"
@ -1316,7 +1316,7 @@ msgstr "Datum Bestandskraft"
msgid "New intervention" msgid "New intervention"
msgstr "Neuer Eingriff" msgstr "Neuer Eingriff"
#: intervention/forms/forms.py:294 #: intervention/forms/forms.py:298
msgid "Edit intervention" msgid "Edit intervention"
msgstr "Eingriff bearbeiten" msgstr "Eingriff bearbeiten"
@ -1392,7 +1392,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:264 konova/forms.py:548 #: intervention/forms/modalForms.py:264 konova/forms.py:581
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."
@ -1637,69 +1637,73 @@ msgstr "Nach Zulassungsbehörde suchen"
msgid "Search for conservation office" msgid "Search for conservation office"
msgstr "Nch Eintragungsstelle suchen" msgstr "Nch Eintragungsstelle suchen"
#: konova/forms.py:39 templates/form/collapsable/form.html:62 #: konova/forms.py:41 templates/form/collapsable/form.html:62
msgid "Save" msgid "Save"
msgstr "Speichern" msgstr "Speichern"
#: konova/forms.py:73 #: konova/forms.py:75
msgid "Not editable" msgid "Not editable"
msgstr "Nicht editierbar" msgstr "Nicht editierbar"
#: konova/forms.py:176 konova/forms.py:348 #: konova/forms.py:178 konova/forms.py:381
msgid "Confirm" msgid "Confirm"
msgstr "Bestätige" msgstr "Bestätige"
#: konova/forms.py:188 konova/forms.py:357 #: konova/forms.py:190 konova/forms.py:390
msgid "Remove" msgid "Remove"
msgstr "Löschen" msgstr "Löschen"
#: konova/forms.py:190 #: konova/forms.py:192
msgid "You are about to remove {} {}" msgid "You are about to remove {} {}"
msgstr "Sie sind dabei {} {} zu löschen" msgstr "Sie sind dabei {} {} zu löschen"
#: konova/forms.py:277 konova/utils/quality.py:44 konova/utils/quality.py:46 #: konova/forms.py:280 konova/utils/quality.py:44 konova/utils/quality.py:46
#: templates/form/collapsable/form.html:45 #: templates/form/collapsable/form.html:45
msgid "Geometry" msgid "Geometry"
msgstr "Geometrie" msgstr "Geometrie"
#: konova/forms.py:358 #: konova/forms.py:325
msgid "Only surfaces allowed. Points or lines must be buffered."
msgstr "Nur Flächen erlaubt. Punkte oder Linien müssen zu Flächen gepuffert werden."
#: konova/forms.py:391
msgid "Are you sure?" msgid "Are you sure?"
msgstr "Sind Sie sicher?" msgstr "Sind Sie sicher?"
#: konova/forms.py:404 #: konova/forms.py:437
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:415 #: konova/forms.py:448
#: 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:417 #: konova/forms.py:450
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:482 #: konova/forms.py:515
msgid "Added document" msgid "Added document"
msgstr "Dokument hinzugefügt" msgstr "Dokument hinzugefügt"
#: konova/forms.py:539 #: konova/forms.py:572
msgid "Confirm record" msgid "Confirm record"
msgstr "Verzeichnen bestätigen" msgstr "Verzeichnen bestätigen"
#: konova/forms.py:547 #: konova/forms.py:580
msgid "Record data" msgid "Record data"
msgstr "Daten verzeichnen" msgstr "Daten verzeichnen"
#: konova/forms.py:554 #: konova/forms.py:587
msgid "Confirm unrecord" msgid "Confirm unrecord"
msgstr "Entzeichnen bestätigen" msgstr "Entzeichnen bestätigen"
#: konova/forms.py:555 #: konova/forms.py:588
msgid "Unrecord data" msgid "Unrecord data"
msgstr "Daten entzeichnen" msgstr "Daten entzeichnen"
#: konova/forms.py:556 #: konova/forms.py:589
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."
@ -2066,7 +2070,7 @@ msgstr "{} wurde erfolgreich vom Nutzer {} geprüft! {}"
msgid "missing" msgid "missing"
msgstr "fehlt" msgstr "fehlt"
#: konova/views.py:96 templates/navbars/navbar.html:16 #: konova/views.py:99 templates/navbars/navbar.html:16
msgid "Home" msgid "Home"
msgstr "Home" msgstr "Home"