Compare commits

..

5 Commits

Author SHA1 Message Date
a86d86b731 # NewEcoAccount EditEcoAccount view
* refactors new and edit eco account views from function to class based
* removes info message if checked intervention is altered and loses the current checked state
* updates comments/documentation
* removes code duplicates
* fixes display error where modal form was hidden behind menu bar of map client
* fixes bug where compensation could not be created directly from intervention
2025-10-20 09:49:18 +02:00
73178b3fd2 # NewCompensation EditCompensation view
* refactors new and edit compensation views from function to class based
* adds checked property to compensation to return parent-intervention's checked info
* fixes bug where compensation could be added to recorded intervention
* updates translations
2025-10-19 14:06:14 +02:00
278a951e92 # NewEma EditEma views
* refactors views for new ema and edit ema from function to class based
* moves shared access check to base edit form view to be checked for every inheriting class
* fixes bug where private variables changed on singleton objects
* updates translations
2025-10-19 13:10:22 +02:00
9e4a78ec60 # EditIntervention view
* refactors edit intervention view from function to class
2025-10-19 12:50:35 +02:00
d03b714fb5 # NewIntervention view
* introduces BaseFormView and BaseNewSpatialLocatedObjectFormView
* refactors new intervention view from function to class
2025-10-19 12:37:13 +02:00
14 changed files with 409 additions and 389 deletions

View File

@ -168,6 +168,17 @@ class NewCompensationForm(AbstractCompensationForm,
comp.log.add(action) comp.log.add(action)
return comp, action return comp, action
def is_valid(self):
valid = super().is_valid()
intervention = self.cleaned_data.get("intervention", None)
valid &= not intervention.is_recorded
if not valid:
self.add_error(
"intervention",
_("This intervention is currently recorded. You cannot add further compensations as long as it is recorded.")
)
return valid
def save(self, user: User, geom_form: SimpleGeomForm): def save(self, user: User, geom_form: SimpleGeomForm):
with transaction.atomic(): with transaction.atomic():
comp, action = self.__create_comp(user) comp, action = self.__create_comp(user)

View File

@ -17,19 +17,20 @@ from compensation.views.compensation.action import NewCompensationActionView, Ed
RemoveCompensationActionView RemoveCompensationActionView
from compensation.views.compensation.state import NewCompensationStateView, EditCompensationStateView, \ from compensation.views.compensation.state import NewCompensationStateView, EditCompensationStateView, \
RemoveCompensationStateView RemoveCompensationStateView
from compensation.views.compensation.compensation import new_view, edit_view, \ from compensation.views.compensation.compensation import \
remove_view, CompensationIndexView, CompensationIdentifierGeneratorView, CompensationDetailView remove_view, CompensationIndexView, CompensationIdentifierGeneratorView, CompensationDetailView, \
NewCompensationFormView, EditCompensationFormView
from compensation.views.compensation.log import CompensationLogView from compensation.views.compensation.log import CompensationLogView
urlpatterns = [ urlpatterns = [
# Main compensation # Main compensation
path("", CompensationIndexView.as_view(), name="index"), path("", CompensationIndexView.as_view(), name="index"),
path('new/id', CompensationIdentifierGeneratorView.as_view(), name='new-id'), path('new/id', CompensationIdentifierGeneratorView.as_view(), name='new-id'),
path('new/<intervention_id>', new_view, name='new'), path('new/<intervention_id>', NewCompensationFormView.as_view(), name='new'),
path('new', new_view, name='new'), path('new', NewCompensationFormView.as_view(), name='new'),
path('<id>', CompensationDetailView.as_view(), name='detail'), path('<id>', CompensationDetailView.as_view(), name='detail'),
path('<id>/log', CompensationLogView.as_view(), name='log'), path('<id>/log', CompensationLogView.as_view(), name='log'),
path('<id>/edit', edit_view, name='edit'), path('<id>/edit', EditCompensationFormView.as_view(), name='edit'),
path('<id>/remove', remove_view, name='remove'), path('<id>/remove', remove_view, name='remove'),
path('<id>/state/new', NewCompensationStateView.as_view(), name='new-state'), path('<id>/state/new', NewCompensationStateView.as_view(), name='new-state'),

View File

@ -8,8 +8,9 @@ Created on: 24.08.21
from django.urls import path from django.urls import path
from compensation.autocomplete.eco_account import EcoAccountAutocomplete from compensation.autocomplete.eco_account import EcoAccountAutocomplete
from compensation.views.eco_account.eco_account import new_view, edit_view, remove_view, \ from compensation.views.eco_account.eco_account import remove_view, \
EcoAccountIndexView, EcoAccountIdentifierGeneratorView, EcoAccountDetailView EcoAccountIndexView, EcoAccountIdentifierGeneratorView, EcoAccountDetailView, NewEcoAccountFormView, \
EditEcoAccountFormView
from compensation.views.eco_account.log import EcoAccountLogView from compensation.views.eco_account.log import EcoAccountLogView
from compensation.views.eco_account.record import EcoAccountRecordView from compensation.views.eco_account.record import EcoAccountRecordView
from compensation.views.eco_account.report import EcoAccountReportView from compensation.views.eco_account.report import EcoAccountReportView
@ -29,13 +30,13 @@ from compensation.views.eco_account.deduction import NewEcoAccountDeductionView,
app_name = "acc" app_name = "acc"
urlpatterns = [ urlpatterns = [
path("", EcoAccountIndexView.as_view(), name="index"), path("", EcoAccountIndexView.as_view(), name="index"),
path('new/', new_view, name='new'), path('new/', NewEcoAccountFormView.as_view(), name='new'),
path('new/id', EcoAccountIdentifierGeneratorView.as_view(), name='new-id'), path('new/id', EcoAccountIdentifierGeneratorView.as_view(), name='new-id'),
path('<id>', EcoAccountDetailView.as_view(), name='detail'), path('<id>', EcoAccountDetailView.as_view(), name='detail'),
path('<id>/log', EcoAccountLogView.as_view(), name='log'), path('<id>/log', EcoAccountLogView.as_view(), name='log'),
path('<id>/record', EcoAccountRecordView.as_view(), name='record'), path('<id>/record', EcoAccountRecordView.as_view(), name='record'),
path('<id>/report', EcoAccountReportView.as_view(), name='report'), path('<id>/report', EcoAccountReportView.as_view(), name='report'),
path('<id>/edit', edit_view, name='edit'), path('<id>/edit', EditEcoAccountFormView.as_view(), name='edit'),
path('<id>/remove', remove_view, name='remove'), path('<id>/remove', remove_view, name='remove'),
path('<id>/resub', EcoAccountResubmissionView.as_view(), name='resubmission-create'), path('<id>/resub', EcoAccountResubmissionView.as_view(), name='resubmission-create'),

View File

@ -18,15 +18,12 @@ from compensation.forms.compensation import EditCompensationForm, NewCompensatio
from compensation.models import Compensation from compensation.models import Compensation
from compensation.tables.compensation import CompensationTable from compensation.tables.compensation import CompensationTable
from intervention.models import Intervention from intervention.models import Intervention
from konova.contexts import BaseContext
from konova.decorators import shared_access_required, default_group_required, login_required_modal from konova.decorators import shared_access_required, default_group_required, login_required_modal
from konova.forms import SimpleGeomForm
from konova.forms.modals import RemoveModalForm from konova.forms.modals import RemoveModalForm
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.message_templates import COMPENSATION_REMOVED_TEMPLATE, DATA_CHECKED_PREVIOUSLY_TEMPLATE, \ from konova.utils.message_templates import COMPENSATION_REMOVED_TEMPLATE, DATA_CHECKED_PREVIOUSLY_TEMPLATE, \
RECORDED_BLOCKS_EDIT, CHECK_STATE_RESET, FORM_INVALID, PARAMS_INVALID, IDENTIFIER_REPLACED, \ RECORDED_BLOCKS_EDIT, PARAMS_INVALID
COMPENSATION_ADDED_TEMPLATE, GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE from konova.views.base import BaseIndexView, BaseIdentifierGeneratorView, BaseNewSpatialLocatedObjectFormView, \
from konova.views.base import BaseIndexView, BaseIdentifierGeneratorView BaseEditSpatialLocatedObjectFormView
from konova.views.detail import BaseDetailView from konova.views.detail import BaseDetailView
@ -44,74 +41,55 @@ class CompensationIndexView(LoginRequiredMixin, BaseIndexView):
return qs return qs
@login_required class NewCompensationFormView(BaseNewSpatialLocatedObjectFormView):
@default_group_required _FORM_CLS = NewCompensationForm
@shared_access_required(Intervention, "intervention_id") _MODEL_CLS = Compensation
def new_view(request: HttpRequest, intervention_id: str = None): _TEMPLATE = "compensation/form/view.html"
""" _TAB_TITLE = _("New Compensation")
Renders a view for a new compensation creation _REDIRECT_URL = "compensation:detail"
Args: def _user_has_shared_access(self, user, **kwargs):
request (HttpRequest): The incoming request # On a new compensation make sure the intervention (if call came directly through an intervention's detail
# view) is shared with the user
Returns: intervention_id = kwargs.get("intervention_id", None)
if not intervention_id:
""" return True
template = "compensation/form/view.html"
if intervention_id is not None:
try:
intervention = Intervention.objects.get(id=intervention_id)
except ObjectDoesNotExist:
messages.error(request, PARAMS_INVALID)
return redirect("home")
if intervention.is_recorded:
messages.info(
request,
RECORDED_BLOCKS_EDIT
)
return redirect("intervention:detail", id=intervention_id)
data_form = NewCompensationForm(request.POST or None, intervention_id=intervention_id)
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
if request.method == "POST":
if data_form.is_valid() and geom_form.is_valid():
generated_identifier = data_form.cleaned_data.get("identifier", None)
comp = data_form.save(request.user, geom_form)
if generated_identifier != comp.identifier:
messages.info(
request,
IDENTIFIER_REPLACED.format(
generated_identifier,
comp.identifier
)
)
messages.success(request, COMPENSATION_ADDED_TEMPLATE.format(comp.identifier))
if geom_form.has_geometry_simplified():
messages.info(
request,
GEOMETRY_SIMPLIFIED
)
num_ignored_geometries = geom_form.get_num_geometries_ignored()
if num_ignored_geometries > 0:
messages.info(
request,
GEOMETRIES_IGNORED_TEMPLATE.format(num_ignored_geometries)
)
return redirect("compensation:detail", id=comp.id)
else: else:
messages.error(request, FORM_INVALID, extra_tags="danger",) intervention = get_object_or_404(Intervention, id=intervention_id)
else: return intervention.is_shared_with(user)
# For clarification: nothing in this case
pass def _user_has_permission(self, user):
context = { # User has to be an ets user
"form": data_form, return user.is_default_user()
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: _("New compensation"), def dispatch(self, request, *args, **kwargs):
} # Make sure there is an existing intervention based on the given id
context = BaseContext(request, context).context # Compensations can not exist without an intervention
return render(request, template, context) intervention_id = kwargs.get("intervention_id", None)
if intervention_id:
try:
intervention = Intervention.objects.get(id=intervention_id)
if intervention.is_recorded:
messages.info(
request,
RECORDED_BLOCKS_EDIT
)
return redirect("intervention:detail", id=intervention_id)
except ObjectDoesNotExist:
messages.error(request, PARAMS_INVALID, extra_tags="danger")
return redirect("home")
return super().dispatch(request, *args, **kwargs)
class EditCompensationFormView(BaseEditSpatialLocatedObjectFormView):
_MODEL_CLS = Compensation
_FORM_CLS = EditCompensationForm
_TEMPLATE = "compensation/form/view.html"
_REDIRECT_URL = "compensation:detail"
def _user_has_permission(self, user):
# User has to be a default user
return user.is_default_user()
class CompensationIdentifierGeneratorView(LoginRequiredMixin, BaseIdentifierGeneratorView): class CompensationIdentifierGeneratorView(LoginRequiredMixin, BaseIdentifierGeneratorView):
@ -119,71 +97,6 @@ class CompensationIdentifierGeneratorView(LoginRequiredMixin, BaseIdentifierGene
_REDIRECT_URL = "compensation:index" _REDIRECT_URL = "compensation:index"
@login_required
@default_group_required
@shared_access_required(Compensation, "id")
def edit_view(request: HttpRequest, id: str):
"""
Renders a view for editing compensations
Args:
request (HttpRequest): The incoming request
Returns:
"""
template = "compensation/form/view.html"
# Get object from db
comp = get_object_or_404(Compensation, id=id)
if comp.is_recorded:
messages.info(
request,
RECORDED_BLOCKS_EDIT
)
return redirect("compensation:detail", id=id)
# Create forms, initialize with values from db/from POST request
data_form = EditCompensationForm(request.POST or None, instance=comp)
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=comp)
if request.method == "POST":
if data_form.is_valid() and geom_form.is_valid():
# Preserve state of intervention checked to determine whether the user must be informed or not
# about a change of the check state
intervention_is_checked = comp.intervention.checked is not None
# The data form takes the geom form for processing, as well as the performing user
comp = data_form.save(request.user, geom_form)
if intervention_is_checked:
messages.info(request, CHECK_STATE_RESET)
messages.success(request, _("Compensation {} edited").format(comp.identifier))
if geom_form.has_geometry_simplified():
messages.info(
request,
GEOMETRY_SIMPLIFIED
)
num_ignored_geometries = geom_form.get_num_geometries_ignored()
if num_ignored_geometries > 0:
messages.info(
request,
GEOMETRIES_IGNORED_TEMPLATE.format(num_ignored_geometries)
)
return redirect("compensation:detail", id=comp.id)
else:
messages.error(request, FORM_INVALID, extra_tags="danger",)
else:
# For clarification: nothing in this case
pass
context = {
"form": data_form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: _("Edit {}").format(comp.identifier),
}
context = BaseContext(request, context).context
return render(request, template, context)
class CompensationDetailView(BaseDetailView): class CompensationDetailView(BaseDetailView):
_MODEL_CLS = Compensation _MODEL_CLS = Compensation
_TEMPLATE = "compensation/detail/compensation/view.html" _TEMPLATE = "compensation/detail/compensation/view.html"

View File

@ -23,7 +23,8 @@ from konova.settings import ETS_GROUP
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.message_templates import CANCEL_ACC_RECORDED_OR_DEDUCTED, RECORDED_BLOCKS_EDIT, FORM_INVALID, \ from konova.utils.message_templates import CANCEL_ACC_RECORDED_OR_DEDUCTED, RECORDED_BLOCKS_EDIT, FORM_INVALID, \
IDENTIFIER_REPLACED, GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE IDENTIFIER_REPLACED, GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE
from konova.views.base import BaseIndexView, BaseIdentifierGeneratorView from konova.views.base import BaseIndexView, BaseIdentifierGeneratorView, BaseNewSpatialLocatedObjectFormView, \
BaseEditSpatialLocatedObjectFormView
from konova.views.detail import BaseDetailView from konova.views.detail import BaseDetailView
@ -40,6 +41,29 @@ class EcoAccountIndexView(LoginRequiredMixin, BaseIndexView):
return qs return qs
class NewEcoAccountFormView(BaseNewSpatialLocatedObjectFormView):
_FORM_CLS = NewEcoAccountForm
_MODEL_CLS = EcoAccount
_TEMPLATE = "compensation/form/view.html"
_TAB_TITLE = _("New Eco-Account")
_REDIRECT_URL = "compensation:acc:detail"
def _user_has_permission(self, user):
# User has to be a default user
return user.is_default_user()
class EditEcoAccountFormView(BaseEditSpatialLocatedObjectFormView):
_FORM_CLS = EditEcoAccountForm
_MODEL_CLS = EcoAccount
_TEMPLATE = "compensation/form/view.html"
_REDIRECT_URL = "compensation:acc:detail"
def _user_has_permission(self, user):
# User has to be a default user
return user.is_default_user()
@login_required @login_required
@default_group_required @default_group_required
def new_view(request: HttpRequest): def new_view(request: HttpRequest):

View File

@ -10,8 +10,8 @@ from django.urls import path
from ema.views.action import NewEmaActionView, EditEmaActionView, RemoveEmaActionView from ema.views.action import NewEmaActionView, EditEmaActionView, RemoveEmaActionView
from ema.views.deadline import NewEmaDeadlineView, EditEmaDeadlineView, RemoveEmaDeadlineView from ema.views.deadline import NewEmaDeadlineView, EditEmaDeadlineView, RemoveEmaDeadlineView
from ema.views.document import NewEmaDocumentView, EditEmaDocumentView, RemoveEmaDocumentView, GetEmaDocumentView from ema.views.document import NewEmaDocumentView, EditEmaDocumentView, RemoveEmaDocumentView, GetEmaDocumentView
from ema.views.ema import new_view, edit_view, remove_view, EmaIndexView, \ from ema.views.ema import remove_view, EmaIndexView, \
EmaIdentifierGeneratorView, EmaDetailView EmaIdentifierGeneratorView, EmaDetailView, EditEmaFormView, NewEmaFormView
from ema.views.log import EmaLogView from ema.views.log import EmaLogView
from ema.views.record import EmaRecordView from ema.views.record import EmaRecordView
from ema.views.report import EmaReportView from ema.views.report import EmaReportView
@ -22,11 +22,11 @@ from ema.views.state import NewEmaStateView, EditEmaStateView, RemoveEmaStateVie
app_name = "ema" app_name = "ema"
urlpatterns = [ urlpatterns = [
path("", EmaIndexView.as_view(), name="index"), path("", EmaIndexView.as_view(), name="index"),
path("new/", new_view, name="new"), path("new/", NewEmaFormView.as_view(), name="new"),
path("new/id", EmaIdentifierGeneratorView.as_view(), name="new-id"), path("new/id", EmaIdentifierGeneratorView.as_view(), name="new-id"),
path("<id>", EmaDetailView.as_view(), name="detail"), path("<id>", EmaDetailView.as_view(), name="detail"),
path('<id>/log', EmaLogView.as_view(), name='log'), path('<id>/log', EmaLogView.as_view(), name='log'),
path('<id>/edit', edit_view, name='edit'), path('<id>/edit', EditEmaFormView.as_view(), name='edit'),
path('<id>/remove', remove_view, name='remove'), path('<id>/remove', remove_view, name='remove'),
path('<id>/record', EmaRecordView.as_view(), name='record'), path('<id>/record', EmaRecordView.as_view(), name='record'),
path('<id>/report', EmaReportView.as_view(), name='report'), path('<id>/report', EmaReportView.as_view(), name='report'),

View File

@ -5,25 +5,20 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
Created on: 19.08.22 Created on: 19.08.22
""" """
from django.contrib import messages
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpRequest from django.http import HttpRequest
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404
from django.urls import reverse from django.urls import reverse
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from ema.forms import NewEmaForm, EditEmaForm from ema.forms import NewEmaForm, EditEmaForm
from ema.models import Ema from ema.models import Ema
from ema.tables import EmaTable from ema.tables import EmaTable
from konova.contexts import BaseContext
from konova.decorators import shared_access_required, conservation_office_group_required, login_required_modal from konova.decorators import shared_access_required, conservation_office_group_required, login_required_modal
from konova.forms import SimpleGeomForm
from konova.forms.modals import RemoveModalForm from konova.forms.modals import RemoveModalForm
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER from konova.views.base import BaseIndexView, BaseIdentifierGeneratorView, BaseNewSpatialLocatedObjectFormView, \
from konova.utils.message_templates import RECORDED_BLOCKS_EDIT, IDENTIFIER_REPLACED, FORM_INVALID, \ BaseEditSpatialLocatedObjectFormView
GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE
from konova.views.base import BaseIndexView, BaseIdentifierGeneratorView
from konova.views.detail import BaseDetailView from konova.views.detail import BaseDetailView
@ -40,59 +35,28 @@ class EmaIndexView(LoginRequiredMixin, BaseIndexView):
return qs return qs
@login_required class NewEmaFormView(BaseNewSpatialLocatedObjectFormView):
@conservation_office_group_required _FORM_CLS = NewEmaForm
def new_view(request: HttpRequest): _MODEL_CLS = Ema
""" _TEMPLATE = "ema/form/view.html"
Renders a view for a new eco account creation _TAB_TITLE = _("New EMA")
_REDIRECT_URL = "ema:detail"
Args: def _user_has_permission(self, user):
request (HttpRequest): The incoming request # User has to be an ets user
return user.is_ets_user()
Returns:
""" class EditEmaFormView(BaseEditSpatialLocatedObjectFormView):
template = "ema/form/view.html" _MODEL_CLS = Ema
data_form = NewEmaForm(request.POST or None) _FORM_CLS = EditEmaForm
geom_form = SimpleGeomForm(request.POST or None, read_only=False) _TEMPLATE = "ema/form/view.html"
if request.method == "POST": _REDIRECT_URL = "ema:detail"
if data_form.is_valid() and geom_form.is_valid(): _TAB_TITLE = _("Edit {}")
generated_identifier = data_form.cleaned_data.get("identifier", None)
ema = data_form.save(request.user, geom_form)
if generated_identifier != ema.identifier:
messages.info(
request,
IDENTIFIER_REPLACED.format(
generated_identifier,
ema.identifier
)
)
messages.success(request, _("EMA {} added").format(ema.identifier))
if geom_form.has_geometry_simplified():
messages.info(
request,
GEOMETRY_SIMPLIFIED
)
num_ignored_geometries = geom_form.get_num_geometries_ignored()
if num_ignored_geometries > 0:
messages.info(
request,
GEOMETRIES_IGNORED_TEMPLATE.format(num_ignored_geometries)
)
return redirect("ema:detail", id=ema.id) def _user_has_permission(self, user):
else: # User has to be an ets user
messages.error(request, FORM_INVALID, extra_tags="danger",) return user.is_ets_user()
else:
# For clarification: nothing in this case
pass
context = {
"form": data_form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: _("New EMA"),
}
context = BaseContext(request, context).context
return render(request, template, context)
class EmaIdentifierGeneratorView(LoginRequiredMixin, BaseIdentifierGeneratorView): class EmaIdentifierGeneratorView(LoginRequiredMixin, BaseIdentifierGeneratorView):
@ -149,65 +113,6 @@ class EmaDetailView(BaseDetailView):
return context return context
@login_required
@conservation_office_group_required
@shared_access_required(Ema, "id")
def edit_view(request: HttpRequest, id: str):
"""
Renders a view for editing compensations
Args:
request (HttpRequest): The incoming request
Returns:
"""
template = "compensation/form/view.html"
# Get object from db
ema = get_object_or_404(Ema, id=id)
if ema.is_recorded:
messages.info(
request,
RECORDED_BLOCKS_EDIT
)
return redirect("ema:detail", id=id)
# Create forms, initialize with values from db/from POST request
data_form = EditEmaForm(request.POST or None, instance=ema)
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=ema)
if request.method == "POST":
if data_form.is_valid() and geom_form.is_valid():
# The data form takes the geom form for processing, as well as the performing user
ema = data_form.save(request.user, geom_form)
messages.success(request, _("EMA {} edited").format(ema.identifier))
if geom_form.has_geometry_simplified():
messages.info(
request,
GEOMETRY_SIMPLIFIED
)
num_ignored_geometries = geom_form.get_num_geometries_ignored()
if num_ignored_geometries > 0:
messages.info(
request,
GEOMETRIES_IGNORED_TEMPLATE.format(num_ignored_geometries)
)
return redirect("ema:detail", id=ema.id)
else:
messages.error(request, FORM_INVALID, extra_tags="danger",)
else:
# For clarification: nothing in this case
pass
context = {
"form": data_form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: _("Edit {}").format(ema.identifier),
}
context = BaseContext(request, context).context
return render(request, template, context)
@login_required_modal @login_required_modal
@login_required @login_required
@conservation_office_group_required @conservation_office_group_required

View File

@ -14,8 +14,9 @@ from intervention.views.deduction import NewInterventionDeductionView, EditInter
RemoveInterventionDeductionView RemoveInterventionDeductionView
from intervention.views.document import NewInterventionDocumentView, GetInterventionDocumentView, \ from intervention.views.document import NewInterventionDocumentView, GetInterventionDocumentView, \
RemoveInterventionDocumentView, EditInterventionDocumentView RemoveInterventionDocumentView, EditInterventionDocumentView
from intervention.views.intervention import new_view, edit_view, remove_view, \ from intervention.views.intervention import remove_view, \
InterventionIndexView, InterventionIdentifierGeneratorView, InterventionDetailView InterventionIndexView, InterventionIdentifierGeneratorView, InterventionDetailView, NewInterventionFormView, \
EditInterventionFormView
from intervention.views.log import InterventionLogView from intervention.views.log import InterventionLogView
from intervention.views.record import InterventionRecordView from intervention.views.record import InterventionRecordView
from intervention.views.report import InterventionReportView from intervention.views.report import InterventionReportView
@ -27,11 +28,11 @@ from intervention.views.share import InterventionShareFormView, InterventionShar
app_name = "intervention" app_name = "intervention"
urlpatterns = [ urlpatterns = [
path("", InterventionIndexView.as_view(), name="index"), path("", InterventionIndexView.as_view(), name="index"),
path('new/', new_view, name='new'), path('new/', NewInterventionFormView.as_view(), name='new'),
path('new/id', InterventionIdentifierGeneratorView.as_view(), name='new-id'), path('new/id', InterventionIdentifierGeneratorView.as_view(), name='new-id'),
path('<id>', InterventionDetailView.as_view(), name='detail'), path('<id>', InterventionDetailView.as_view(), name='detail'),
path('<id>/log', InterventionLogView.as_view(), name='log'), path('<id>/log', InterventionLogView.as_view(), name='log'),
path('<id>/edit', edit_view, name='edit'), path('<id>/edit', EditInterventionFormView.as_view(), name='edit'),
path('<id>/remove', remove_view, name='remove'), path('<id>/remove', remove_view, name='remove'),
path('<id>/share/<token>', InterventionShareByTokenView.as_view(), name='share-token'), path('<id>/share/<token>', InterventionShareByTokenView.as_view(), name='share-token'),
path('<id>/share', InterventionShareFormView.as_view(), name='share-form'), path('<id>/share', InterventionShareFormView.as_view(), name='share-form'),

View File

@ -8,7 +8,7 @@ Created on: 19.08.22
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import JsonResponse, HttpRequest from django.http import HttpRequest
from django.shortcuts import get_object_or_404, render, redirect from django.shortcuts import get_object_or_404, render, redirect
from django.urls import reverse from django.urls import reverse
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -17,16 +17,14 @@ from intervention.forms.intervention import EditInterventionForm, NewInterventio
from intervention.models import Intervention from intervention.models import Intervention
from intervention.tables import InterventionTable from intervention.tables import InterventionTable
from konova.contexts import BaseContext from konova.contexts import BaseContext
from konova.decorators import default_group_required, shared_access_required, any_group_check, login_required_modal, \ from konova.decorators import default_group_required, shared_access_required, login_required_modal
uuid_required
from konova.forms import SimpleGeomForm from konova.forms import SimpleGeomForm
from konova.forms.modals import RemoveModalForm from konova.forms.modals import RemoveModalForm
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.message_templates import DATA_CHECKED_PREVIOUSLY_TEMPLATE, RECORDED_BLOCKS_EDIT, \ from konova.utils.message_templates import DATA_CHECKED_PREVIOUSLY_TEMPLATE, RECORDED_BLOCKS_EDIT, \
CHECK_STATE_RESET, FORM_INVALID, IDENTIFIER_REPLACED, DO_NOT_FORGET_TO_SHARE, GEOMETRY_SIMPLIFIED, \ CHECK_STATE_RESET, FORM_INVALID, GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE
GEOMETRIES_IGNORED_TEMPLATE from konova.views.base import BaseIndexView, BaseIdentifierGeneratorView, BaseNewSpatialLocatedObjectFormView, \
from konova.views.base import BaseIndexView, BaseIdentifierGeneratorView BaseEditSpatialLocatedObjectFormView
from konova.views.detail import BaseDetailView from konova.views.detail import BaseDetailView
@ -45,60 +43,20 @@ class InterventionIndexView(LoginRequiredMixin, BaseIndexView):
return qs return qs
@login_required class NewInterventionFormView(BaseNewSpatialLocatedObjectFormView):
@default_group_required _MODEL_CLS = Intervention
def new_view(request: HttpRequest): _FORM_CLS = NewInterventionForm
""" _TEMPLATE = "intervention/form/view.html"
Renders a view for a new intervention creation _REDIRECT_URL = "intervention:detail"
_TAB_TITLE = _("New intervention")
Args:
request (HttpRequest): The incoming request
Returns: class EditInterventionFormView(BaseEditSpatialLocatedObjectFormView):
_MODEL_CLS = Intervention
""" _FORM_CLS = EditInterventionForm
template = "intervention/form/view.html" _TEMPLATE = "intervention/form/view.html"
data_form = NewInterventionForm(request.POST or None) _REDIRECT_URL = "intervention:detail"
geom_form = SimpleGeomForm(request.POST or None, read_only=False) _TAB_TITLE = _("Edit {}")
if request.method == "POST":
if data_form.is_valid() and geom_form.is_valid():
generated_identifier = data_form.cleaned_data.get("identifier", None)
intervention = data_form.save(request.user, geom_form)
if generated_identifier != intervention.identifier:
messages.info(
request,
IDENTIFIER_REPLACED.format(
generated_identifier,
intervention.identifier
)
)
messages.success(request, _("Intervention {} added").format(intervention.identifier))
if geom_form.has_geometry_simplified():
messages.info(
request,
GEOMETRY_SIMPLIFIED
)
num_ignored_geometries = geom_form.get_num_geometries_ignored()
if num_ignored_geometries > 0:
messages.info(
request,
GEOMETRIES_IGNORED_TEMPLATE.format(num_ignored_geometries)
)
return redirect("intervention:detail", id=intervention.id)
else:
messages.error(request, FORM_INVALID, extra_tags="danger",)
else:
# For clarification: nothing in this case
pass
context = {
"form": data_form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: _("New intervention"),
}
context = BaseContext(request, context).context
return render(request, template, context)
class InterventionIdentifierGeneratorView(LoginRequiredMixin, BaseIdentifierGeneratorView): class InterventionIdentifierGeneratorView(LoginRequiredMixin, BaseIdentifierGeneratorView):

View File

@ -25,6 +25,7 @@ class BaseForm(forms.Form):
cancel_redirect = None cancel_redirect = None
form_caption = None form_caption = None
instance = None # The data holding model object instance = None # The data holding model object
user = None # The performing user
request = None request = None
form_attrs = {} # Holds additional attributes, that can be used in the template form_attrs = {} # Holds additional attributes, that can be used in the template
has_required_fields = False # Automatically set. Triggers hint rendering in templates has_required_fields = False # Automatically set. Triggers hint rendering in templates
@ -33,6 +34,7 @@ class BaseForm(forms.Form):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.instance = kwargs.pop("instance", None) self.instance = kwargs.pop("instance", None)
self.user = kwargs.pop("user", None)
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
if self.request is not None: if self.request is not None:
self.user = self.request.user self.user = self.request.user
@ -46,7 +48,7 @@ class BaseForm(forms.Form):
self.__check_valid_label_input_ratio() self.__check_valid_label_input_ratio()
@abstractmethod @abstractmethod
def save(self): def save(self, *arg, **kwargs):
# To be implemented in subclasses! # To be implemented in subclasses!
pass pass

View File

@ -288,4 +288,8 @@ Overwrites netgis.css attributes
Overwrites gradient used on default css of netgis map client Overwrites gradient used on default css of netgis map client
*/ */
background: var(--rlp-red) !important; background: var(--rlp-red) !important;
}
.modal{
z-index: 100000;
} }

View File

@ -6,15 +6,19 @@ Created on: 15.10.25
from abc import abstractmethod from abc import abstractmethod
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpRequest, JsonResponse from django.http import HttpRequest, JsonResponse
from django.shortcuts import render, redirect from django.shortcuts import render, redirect, get_object_or_404
from django.urls import reverse from django.urls import reverse
from django.views import View from django.views import View
from django.utils.translation import gettext_lazy as _
from konova.contexts import BaseContext from konova.contexts import BaseContext
from konova.forms import BaseForm, SimpleGeomForm
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.general import check_user_is_in_any_group from konova.utils.general import check_user_is_in_any_group
from konova.utils.message_templates import MISSING_GROUP_PERMISSION, DATA_UNSHARED from konova.utils.message_templates import MISSING_GROUP_PERMISSION, DATA_UNSHARED, IDENTIFIER_REPLACED, \
GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE, RECORDED_BLOCKS_EDIT, CHECK_STATE_RESET, FORM_INVALID
class BaseView(View): class BaseView(View):
@ -139,3 +143,183 @@ class BaseIdentifierGeneratorView(BaseView):
def _user_has_shared_access(self, user, **kwargs): def _user_has_shared_access(self, user, **kwargs):
# No specific constraints for shared access # No specific constraints for shared access
return True return True
class BaseFormView(BaseView):
_MODEL_CLS = None
_FORM_CLS = None
class Meta:
abstract = True
def _get_additional_context(self, **kwargs):
"""
Args:
**kwargs ():
Returns:
"""
return {}
class BaseSpatialLocatedObjectFormView(LoginRequiredMixin, BaseFormView):
_GEOMETRY_FORM_CLS = SimpleGeomForm
class Meta:
abstract = True
class BaseNewSpatialLocatedObjectFormView(BaseSpatialLocatedObjectFormView):
def _user_has_permission(self, user):
# User has to have default privilege to call this endpoint
return user.is_default_user()
def _user_has_shared_access(self, user, **kwargs):
# There is no shared access control since nothing exists yet
return True
def get(self, request: HttpRequest, **kwargs):
form: BaseForm = self._FORM_CLS(None, **kwargs, user=request.user)
geom_form: SimpleGeomForm = self._GEOMETRY_FORM_CLS(None, user=request.user, read_only=False)
context = self._get_additional_context()
context = BaseContext(request, additional_context=context).context
context.update(
{
"form": form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: self._TAB_TITLE,
}
)
return render(request, self._TEMPLATE, context)
def post(self, request: HttpRequest, **kwargs):
form: BaseForm = self._FORM_CLS(request.POST or None, **kwargs, user=request.user)
geom_form: SimpleGeomForm = self._GEOMETRY_FORM_CLS(request.POST or None, user=request.user, read_only=False)
if form.is_valid() and geom_form.is_valid():
obj = form.save(request.user, geom_form)
obj_redirect_url = reverse(self._REDIRECT_URL, args=(obj.id,))
generated_identifier = form.cleaned_data.get("identifier", None)
if generated_identifier != obj.identifier:
messages.info(
request,
IDENTIFIER_REPLACED.format(
generated_identifier,
obj.identifier
)
)
messages.success(request, _("{} added").format(obj.identifier))
if geom_form.has_geometry_simplified():
messages.info(
request,
GEOMETRY_SIMPLIFIED
)
num_ignored_geometries = geom_form.get_num_geometries_ignored()
if num_ignored_geometries > 0:
messages.info(
request,
GEOMETRIES_IGNORED_TEMPLATE.format(num_ignored_geometries)
)
return redirect(obj_redirect_url)
else:
context = self._get_additional_context()
messages.error(request, FORM_INVALID, extra_tags="danger",)
context = BaseContext(request, additional_context=context).context
context.update(
{
"form": form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: self._TAB_TITLE,
}
)
return render(request, self._TEMPLATE, context)
class BaseEditSpatialLocatedObjectFormView(BaseSpatialLocatedObjectFormView):
_TAB_TITLE = _("Edit {}")
def get(self, request: HttpRequest, id: str):
obj = get_object_or_404(
self._MODEL_CLS,
id=id
)
obj_redirect_url = reverse(self._REDIRECT_URL, args=(obj.id,))
if obj.is_recorded:
messages.info(
request,
RECORDED_BLOCKS_EDIT
)
return redirect(obj_redirect_url)
form: BaseForm = self._FORM_CLS(None, instance=obj, user=request.user)
geom_form: SimpleGeomForm = self._GEOMETRY_FORM_CLS(None, instance=obj, read_only=False)
context = self._get_additional_context()
context = BaseContext(request, additional_context=context).context
context.update(
{
"form": form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: self._TAB_TITLE.format(obj.identifier),
}
)
return render(request, self._TEMPLATE, context)
def post(self, request: HttpRequest, id: str):
obj = get_object_or_404(
self._MODEL_CLS,
id=id
)
obj_redirect_url = reverse(self._REDIRECT_URL, args=(obj.id,))
form: BaseForm = self._FORM_CLS(request.POST or None, instance=obj, user=request.user)
geom_form: SimpleGeomForm = self._GEOMETRY_FORM_CLS(request.POST or None, instance=obj, read_only=False)
if form.is_valid() and geom_form.is_valid():
obj = form.save(request.user, geom_form)
messages.success(request, _("{} edited").format(obj.identifier))
if geom_form.has_geometry_simplified():
messages.info(
request,
GEOMETRY_SIMPLIFIED
)
num_ignored_geometries = geom_form.get_num_geometries_ignored()
if num_ignored_geometries > 0:
messages.info(
request,
GEOMETRIES_IGNORED_TEMPLATE.format(num_ignored_geometries)
)
return redirect(obj_redirect_url)
else:
context = self._get_additional_context()
messages.error(request, FORM_INVALID, extra_tags="danger",)
context = BaseContext(request, additional_context=context).context
context.update(
{
"form": form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: self._TAB_TITLE.format(obj.identifier),
}
)
return render(request, self._TEMPLATE, context)
def _user_has_shared_access(self, user, **kwargs):
obj = get_object_or_404(self._MODEL_CLS, id=kwargs.get('id', None))
return obj.is_shared_with(user)
def _user_has_permission(self, user):
return user.is_default_user()

Binary file not shown.

View File

@ -45,7 +45,7 @@ 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: 2025-10-15 09:11+0200\n" "POT-Creation-Date: 2025-10-19 13:56+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"
@ -448,11 +448,19 @@ msgid "Select the intervention for which this compensation compensates"
msgstr "Wählen Sie den Eingriff, für den diese Kompensation bestimmt ist" msgstr "Wählen Sie den Eingriff, für den diese Kompensation bestimmt ist"
#: compensation/forms/compensation.py:114 #: compensation/forms/compensation.py:114
#: compensation/views/compensation/compensation.py:120 #: compensation/views/compensation/compensation.py:161
msgid "New compensation" msgid "New compensation"
msgstr "Neue Kompensation" msgstr "Neue Kompensation"
#: compensation/forms/compensation.py:190 #: compensation/forms/compensation.py:179
msgid ""
"This intervention is currently recorded. You cannot add further "
"compensations as long as it is recorded."
msgstr ""
"Dieser Eingriff ist derzeit verzeichnet. "
"Sie können keine weiteren Kompensationen hinzufügen, so lange er verzeichnet ist."
#: compensation/forms/compensation.py:202
msgid "Edit compensation" msgid "Edit compensation"
msgstr "Bearbeite Kompensation" msgstr "Bearbeite Kompensation"
@ -475,7 +483,7 @@ msgid "When did the parties agree on this?"
msgstr "Wann wurde dieses Ökokonto offiziell vereinbart?" msgstr "Wann wurde dieses Ökokonto offiziell vereinbart?"
#: compensation/forms/eco_account.py:72 #: compensation/forms/eco_account.py:72
#: compensation/views/eco_account/eco_account.py:101 #: compensation/views/eco_account/eco_account.py:93
msgid "New Eco-Account" msgid "New Eco-Account"
msgstr "Neues Ökokonto" msgstr "Neues Ökokonto"
@ -1288,44 +1296,45 @@ msgstr ""
msgid "Responsible data" msgid "Responsible data"
msgstr "Daten zu den verantwortlichen Stellen" msgstr "Daten zu den verantwortlichen Stellen"
#: compensation/views/compensation/compensation.py:58 #: compensation/views/compensation/compensation.py:35
msgid "Compensations - Overview" msgid "Compensations - Overview"
msgstr "Kompensationen - Übersicht" msgstr "Kompensationen - Übersicht"
#: compensation/views/compensation/compensation.py:181 #: compensation/views/compensation/compensation.py:52
#, fuzzy
#| msgid "New compensation"
msgid "New Compensation"
msgstr "Neue Kompensation"
#: compensation/views/compensation/compensation.py:208
#: konova/utils/message_templates.py:40 #: konova/utils/message_templates.py:40
msgid "Compensation {} edited" msgid "Compensation {} edited"
msgstr "Kompensation {} bearbeitet" msgstr "Kompensation {} bearbeitet"
#: compensation/views/compensation/compensation.py:196 #: compensation/views/compensation/compensation.py:231
#: compensation/views/eco_account/eco_account.py:173 ema/views/ema.py:238 #: compensation/views/eco_account/eco_account.py:159 ema/views/ema.py:59
#: intervention/views/intervention.py:253 #: intervention/views/intervention.py:59 intervention/views/intervention.py:179
#: konova/views/base.py:239
msgid "Edit {}" msgid "Edit {}"
msgstr "Bearbeite {}" msgstr "Bearbeite {}"
#: compensation/views/compensation/report.py:35 #: compensation/views/eco_account/eco_account.py:32
#: compensation/views/eco_account/report.py:36 ema/views/report.py:35
#: intervention/views/report.py:35
msgid "Report {}"
msgstr "Bericht {}"
#: compensation/views/eco_account/eco_account.py:53
msgid "Eco-account - Overview" msgid "Eco-account - Overview"
msgstr "Ökokonten - Übersicht" msgstr "Ökokonten - Übersicht"
#: compensation/views/eco_account/eco_account.py:86 #: compensation/views/eco_account/eco_account.py:70
msgid "Eco-Account {} added" msgid "Eco-Account {} added"
msgstr "Ökokonto {} hinzugefügt" msgstr "Ökokonto {} hinzugefügt"
#: compensation/views/eco_account/eco_account.py:158 #: compensation/views/eco_account/eco_account.py:136
msgid "Eco-Account {} edited" msgid "Eco-Account {} edited"
msgstr "Ökokonto {} bearbeitet" msgstr "Ökokonto {} bearbeitet"
#: compensation/views/eco_account/eco_account.py:288 #: compensation/views/eco_account/eco_account.py:260
msgid "Eco-account removed" msgid "Eco-account removed"
msgstr "Ökokonto entfernt" msgstr "Ökokonto entfernt"
#: ema/forms.py:42 ema/tests/unit/test_forms.py:27 ema/views/ema.py:108 #: ema/forms.py:42 ema/tests/unit/test_forms.py:27 ema/views/ema.py:42
msgid "New EMA" msgid "New EMA"
msgstr "Neue EMA hinzufügen" msgstr "Neue EMA hinzufügen"
@ -1353,19 +1362,11 @@ msgstr ""
msgid "Payment funded compensation" msgid "Payment funded compensation"
msgstr "Ersatzzahlungsmaßnahme" msgstr "Ersatzzahlungsmaßnahme"
#: ema/views/ema.py:53 #: ema/views/ema.py:26
msgid "EMAs - Overview" msgid "EMAs - Overview"
msgstr "EMAs - Übersicht" msgstr "EMAs - Übersicht"
#: ema/views/ema.py:86 #: ema/views/ema.py:138
msgid "EMA {} added"
msgstr "EMA {} hinzugefügt"
#: ema/views/ema.py:223
msgid "EMA {} edited"
msgstr "EMA {} bearbeitet"
#: ema/views/ema.py:262
msgid "EMA removed" msgid "EMA removed"
msgstr "EMA entfernt" msgstr "EMA entfernt"
@ -1429,7 +1430,7 @@ msgstr "Datum Bestandskraft bzw. Rechtskraft"
#: intervention/forms/intervention.py:216 #: intervention/forms/intervention.py:216
#: intervention/tests/unit/test_forms.py:36 #: intervention/tests/unit/test_forms.py:36
#: intervention/views/intervention.py:105 #: intervention/views/intervention.py:51
msgid "New intervention" msgid "New intervention"
msgstr "Neuer Eingriff" msgstr "Neuer Eingriff"
@ -1665,19 +1666,15 @@ msgstr ""
msgid "Check performed" msgid "Check performed"
msgstr "Prüfung durchgeführt" msgstr "Prüfung durchgeführt"
#: intervention/views/intervention.py:57 #: intervention/views/intervention.py:33
msgid "Interventions - Overview" msgid "Interventions - Overview"
msgstr "Eingriffe - Übersicht" msgstr "Eingriffe - Übersicht"
#: intervention/views/intervention.py:90 #: intervention/views/intervention.py:154
msgid "Intervention {} added"
msgstr "Eingriff {} hinzugefügt"
#: intervention/views/intervention.py:236
msgid "Intervention {} edited" msgid "Intervention {} edited"
msgstr "Eingriff {} bearbeitet" msgstr "Eingriff {} bearbeitet"
#: intervention/views/intervention.py:278 #: intervention/views/intervention.py:204
msgid "{} removed" msgid "{} removed"
msgstr "{} entfernt" msgstr "{} entfernt"
@ -1689,7 +1686,7 @@ msgstr "Hierfür müssen Sie Mitarbeiter sein!"
msgid "You need to be administrator to perform this action!" msgid "You need to be administrator to perform this action!"
msgstr "Hierfür müssen Sie Administrator sein!" msgstr "Hierfür müssen Sie Administrator sein!"
#: konova/decorators.py:65 #: konova/decorators.py:65 konova/utils/general.py:40
msgid "" msgid ""
"+++ Attention: You are not part of any group. You won't be able to create, " "+++ Attention: You are not part of any group. You won't be able to create, "
"edit or do anything. Please contact an administrator. +++" "edit or do anything. Please contact an administrator. +++"
@ -1801,7 +1798,7 @@ msgstr "Sucht nach Einträgen, an denen diese Person gearbeitet hat"
msgid "Save" msgid "Save"
msgstr "Speichern" msgstr "Speichern"
#: konova/forms/base_form.py:72 #: konova/forms/base_form.py:74
msgid "Not editable" msgid "Not editable"
msgstr "Nicht editierbar" msgstr "Nicht editierbar"
@ -1810,7 +1807,7 @@ msgstr "Nicht editierbar"
msgid "Geometry" msgid "Geometry"
msgstr "Geometrie" msgstr "Geometrie"
#: konova/forms/geometry_form.py:100 #: konova/forms/geometry_form.py:101
msgid "Only surfaces allowed. Points or lines must be buffered." msgid "Only surfaces allowed. Points or lines must be buffered."
msgstr "" msgstr ""
"Nur Flächen erlaubt. Punkte oder Linien müssen zu Flächen gepuffert werden." "Nur Flächen erlaubt. Punkte oder Linien müssen zu Flächen gepuffert werden."
@ -2268,8 +2265,9 @@ msgid ""
"too small to be valid). These parts have been removed. Please check the " "too small to be valid). These parts have been removed. Please check the "
"stored geometry." "stored geometry."
msgstr "" msgstr ""
"Die Geometrie enthielt {} invalide Bestandteile (z.B. unaussagekräftige Kleinstflächen)." "Die Geometrie enthielt {} invalide Bestandteile (z.B. unaussagekräftige "
"Diese Bestandteile wurden automatisch entfernt. Bitte überprüfen Sie die angepasste Geometrie." "Kleinstflächen).Diese Bestandteile wurden automatisch entfernt. Bitte "
"überprüfen Sie die angepasste Geometrie."
#: konova/utils/message_templates.py:89 #: konova/utils/message_templates.py:89
msgid "This intervention has {} revocations" msgid "This intervention has {} revocations"
@ -2310,7 +2308,15 @@ msgstr ""
"Dieses Datum ist unrealistisch. Geben Sie bitte das korrekte Datum ein " "Dieses Datum ist unrealistisch. Geben Sie bitte das korrekte Datum ein "
"(>1950)." "(>1950)."
#: konova/views/home.py:75 templates/navbars/navbar.html:16 #: konova/views/base.py:209
msgid "{} added"
msgstr "{} hinzugefügt"
#: konova/views/base.py:281
msgid "{} edited"
msgstr "{} bearbeitet"
#: konova/views/home.py:72 templates/navbars/navbar.html:16
msgid "Home" msgid "Home"
msgstr "Home" msgstr "Home"
@ -2330,6 +2336,10 @@ msgstr "{} verzeichnet"
msgid "Errors found:" msgid "Errors found:"
msgstr "Fehler gefunden:" msgstr "Fehler gefunden:"
#: konova/views/report.py:21
msgid "Report {}"
msgstr "Bericht {}"
#: konova/views/resubmission.py:39 #: konova/views/resubmission.py:39
msgid "Resubmission set" msgid "Resubmission set"
msgstr "Wiedervorlage gesetzt" msgstr "Wiedervorlage gesetzt"
@ -3056,7 +3066,7 @@ msgid "Manage teams"
msgstr "" msgstr ""
#: user/templates/user/index.html:53 user/templates/user/team/index.html:19 #: user/templates/user/index.html:53 user/templates/user/team/index.html:19
#: user/views/views.py:135 #: user/views/views.py:134
msgid "Teams" msgid "Teams"
msgstr "" msgstr ""
@ -3116,34 +3126,40 @@ msgstr "Läuft ab am"
msgid "User API token" msgid "User API token"
msgstr "API Nutzer Token" msgstr "API Nutzer Token"
#: user/views/views.py:33 #: user/views/views.py:31
msgid "User settings" msgid "User settings"
msgstr "Einstellungen" msgstr "Einstellungen"
#: user/views/views.py:59 #: user/views/views.py:44
msgid "Notifications edited"
msgstr "Benachrichtigungen bearbeitet"
#: user/views/views.py:71
msgid "User notifications" msgid "User notifications"
msgstr "Benachrichtigungen" msgstr "Benachrichtigungen"
#: user/views/views.py:147 #: user/views/views.py:64
msgid "Notifications edited"
msgstr "Benachrichtigungen bearbeitet"
#: user/views/views.py:152
msgid "New team added" msgid "New team added"
msgstr "Neues Team hinzugefügt" msgstr "Neues Team hinzugefügt"
#: user/views/views.py:162 #: user/views/views.py:167
msgid "Team edited" msgid "Team edited"
msgstr "Team bearbeitet" msgstr "Team bearbeitet"
#: user/views/views.py:177 #: user/views/views.py:182
msgid "Team removed" msgid "Team removed"
msgstr "Team gelöscht" msgstr "Team gelöscht"
#: user/views/views.py:192 #: user/views/views.py:197
msgid "You are not a member of this team" msgid "You are not a member of this team"
msgstr "Sie sind kein Mitglied dieses Teams" msgstr "Sie sind kein Mitglied dieses Teams"
#: user/views/views.py:199 #: user/views/views.py:204
msgid "Left Team" msgid "Left Team"
msgstr "Team verlassen" msgstr "Team verlassen"
#~ msgid "EMA {} added"
#~ msgstr "EMA {} hinzugefügt"
#~ msgid "EMA {} edited"
#~ msgstr "EMA {} bearbeitet"