# Renaming
* renames certain classes to match their content * splits larger files into smaller ones
This commit is contained in:
parent
c4cd40913d
commit
e49eed21da
@ -237,7 +237,11 @@ class EditEcoAccountForm(NewEcoAccountForm):
|
||||
|
||||
|
||||
class RemoveEcoAccountModalForm(RemoveModalForm):
|
||||
""" Form class
|
||||
|
||||
Provides a form for deleting eco accounts
|
||||
|
||||
"""
|
||||
def is_valid(self):
|
||||
super_valid = super().is_valid()
|
||||
has_deductions = self.instance.deductions.exists()
|
||||
|
||||
@ -6,29 +6,26 @@ Created on: 19.08.22
|
||||
|
||||
"""
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.http import HttpRequest, JsonResponse
|
||||
from django.shortcuts import get_object_or_404, render, redirect
|
||||
from django.urls import reverse
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from compensation.forms.compensation import EditCompensationForm, NewCompensationForm
|
||||
from compensation.models import Compensation
|
||||
from compensation.tables.compensation import CompensationTable
|
||||
from intervention.models import Intervention
|
||||
from konova.decorators import shared_access_required, default_group_required, login_required_modal
|
||||
from konova.forms.modals import RemoveModalForm
|
||||
from konova.utils.message_templates import COMPENSATION_REMOVED_TEMPLATE, DATA_CHECKED_PREVIOUSLY_TEMPLATE, \
|
||||
from konova.utils.message_templates import DATA_CHECKED_PREVIOUSLY_TEMPLATE, \
|
||||
RECORDED_BLOCKS_EDIT, PARAMS_INVALID
|
||||
from konova.views.base import BaseIndexView, BaseIdentifierGeneratorView, BaseNewSpatialLocatedObjectFormView, \
|
||||
BaseEditSpatialLocatedObjectFormView
|
||||
from konova.views.identifier import AbstractIdentifierGeneratorView
|
||||
from konova.views.form import AbstractNewGeometryFormView, AbstractEditGeometryFormView
|
||||
from konova.views.index import AbstractIndexView
|
||||
from konova.views.detail import BaseDetailView
|
||||
from konova.views.remove import BaseRemoveModalFormView
|
||||
|
||||
|
||||
class CompensationIndexView(LoginRequiredMixin, BaseIndexView):
|
||||
class CompensationIndexView(LoginRequiredMixin, AbstractIndexView):
|
||||
_TAB_TITLE = _("Compensations - Overview")
|
||||
_INDEX_TABLE_CLS = CompensationTable
|
||||
|
||||
@ -42,7 +39,7 @@ class CompensationIndexView(LoginRequiredMixin, BaseIndexView):
|
||||
return qs
|
||||
|
||||
|
||||
class NewCompensationFormView(BaseNewSpatialLocatedObjectFormView):
|
||||
class NewCompensationFormView(AbstractNewGeometryFormView):
|
||||
_FORM_CLS = NewCompensationForm
|
||||
_MODEL_CLS = Compensation
|
||||
_TEMPLATE = "compensation/form/view.html"
|
||||
@ -82,7 +79,7 @@ class NewCompensationFormView(BaseNewSpatialLocatedObjectFormView):
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
|
||||
class EditCompensationFormView(BaseEditSpatialLocatedObjectFormView):
|
||||
class EditCompensationFormView(AbstractEditGeometryFormView):
|
||||
_MODEL_CLS = Compensation
|
||||
_FORM_CLS = EditCompensationForm
|
||||
_TEMPLATE = "compensation/form/view.html"
|
||||
@ -93,7 +90,7 @@ class EditCompensationFormView(BaseEditSpatialLocatedObjectFormView):
|
||||
return user.is_default_user()
|
||||
|
||||
|
||||
class CompensationIdentifierGeneratorView(LoginRequiredMixin, BaseIdentifierGeneratorView):
|
||||
class CompensationIdentifierGeneratorView(LoginRequiredMixin, AbstractIdentifierGeneratorView):
|
||||
_MODEL_CLS = Compensation
|
||||
_REDIRECT_URL = "compensation:index"
|
||||
|
||||
|
||||
@ -10,10 +10,10 @@ from django.urls import reverse
|
||||
from compensation.models import Compensation
|
||||
from konova.sub_settings.django_settings import BASE_URL
|
||||
from konova.utils.qrcode import QrCode
|
||||
from konova.views.report import BaseReportView
|
||||
from konova.views.report import AbstractReportView
|
||||
|
||||
|
||||
class BaseCompensationReportView(BaseReportView):
|
||||
class BaseCompensationReportView(AbstractReportView):
|
||||
def _get_compensation_report_context(self, obj):
|
||||
# Order states by surface
|
||||
before_states = obj.before_states.all().order_by("-surface").prefetch_related("biotope_type")
|
||||
|
||||
@ -5,31 +5,24 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||
Created on: 19.08.22
|
||||
|
||||
"""
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.http import HttpRequest
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
from django.urls import reverse
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from compensation.forms.eco_account import EditEcoAccountForm, NewEcoAccountForm, RemoveEcoAccountModalForm
|
||||
from compensation.models import EcoAccount
|
||||
from compensation.tables.eco_account import EcoAccountTable
|
||||
from konova.contexts import BaseContext
|
||||
from konova.decorators import shared_access_required, default_group_required, login_required_modal
|
||||
from konova.forms import SimpleGeomForm
|
||||
from konova.settings import ETS_GROUP
|
||||
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, \
|
||||
IDENTIFIER_REPLACED, GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE
|
||||
from konova.views.base import BaseIndexView, BaseIdentifierGeneratorView, BaseNewSpatialLocatedObjectFormView, \
|
||||
BaseEditSpatialLocatedObjectFormView
|
||||
from konova.views.identifier import AbstractIdentifierGeneratorView
|
||||
from konova.views.form import AbstractNewGeometryFormView, AbstractEditGeometryFormView
|
||||
from konova.views.index import AbstractIndexView
|
||||
from konova.views.detail import BaseDetailView
|
||||
from konova.views.remove import BaseRemoveModalFormView
|
||||
|
||||
|
||||
class EcoAccountIndexView(LoginRequiredMixin, BaseIndexView):
|
||||
class EcoAccountIndexView(LoginRequiredMixin, AbstractIndexView):
|
||||
""" View class for indexing eco accounts
|
||||
|
||||
"""
|
||||
_INDEX_TABLE_CLS = EcoAccountTable
|
||||
_TAB_TITLE = _("Eco-account - Overview")
|
||||
|
||||
@ -42,7 +35,12 @@ class EcoAccountIndexView(LoginRequiredMixin, BaseIndexView):
|
||||
return qs
|
||||
|
||||
|
||||
class NewEcoAccountFormView(BaseNewSpatialLocatedObjectFormView):
|
||||
class NewEcoAccountFormView(AbstractNewGeometryFormView):
|
||||
""" Form view class
|
||||
|
||||
Renders a form for new eco accounts
|
||||
|
||||
"""
|
||||
_FORM_CLS = NewEcoAccountForm
|
||||
_MODEL_CLS = EcoAccount
|
||||
_TEMPLATE = "compensation/form/view.html"
|
||||
@ -54,7 +52,12 @@ class NewEcoAccountFormView(BaseNewSpatialLocatedObjectFormView):
|
||||
return user.is_default_user()
|
||||
|
||||
|
||||
class EditEcoAccountFormView(BaseEditSpatialLocatedObjectFormView):
|
||||
class EditEcoAccountFormView(AbstractEditGeometryFormView):
|
||||
""" Form view class
|
||||
|
||||
Renders a form for editing of eco accounts
|
||||
|
||||
"""
|
||||
_FORM_CLS = EditEcoAccountForm
|
||||
_MODEL_CLS = EcoAccount
|
||||
_TEMPLATE = "compensation/form/view.html"
|
||||
@ -65,129 +68,20 @@ class EditEcoAccountFormView(BaseEditSpatialLocatedObjectFormView):
|
||||
return user.is_default_user()
|
||||
|
||||
|
||||
@login_required
|
||||
@default_group_required
|
||||
def new_view(request: HttpRequest):
|
||||
"""
|
||||
Renders a view for a new eco account creation
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
|
||||
Returns:
|
||||
class EcoAccountIdentifierGeneratorView(LoginRequiredMixin, AbstractIdentifierGeneratorView):
|
||||
""" View class for identifier generation on eco accounts
|
||||
|
||||
"""
|
||||
template = "compensation/form/view.html"
|
||||
data_form = NewEcoAccountForm(request.POST or None)
|
||||
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)
|
||||
acc = data_form.save(request.user, geom_form)
|
||||
if generated_identifier != acc.identifier:
|
||||
messages.info(
|
||||
request,
|
||||
IDENTIFIER_REPLACED.format(
|
||||
generated_identifier,
|
||||
acc.identifier
|
||||
)
|
||||
)
|
||||
messages.success(request, _("Eco-Account {} added").format(acc.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:acc:detail", id=acc.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 Eco-Account"),
|
||||
}
|
||||
context = BaseContext(request, context).context
|
||||
return render(request, template, context)
|
||||
|
||||
|
||||
class EcoAccountIdentifierGeneratorView(LoginRequiredMixin, BaseIdentifierGeneratorView):
|
||||
_MODEL_CLS = EcoAccount
|
||||
_REDIRECT_URL = "compensation:acc:index"
|
||||
|
||||
|
||||
@login_required
|
||||
@default_group_required
|
||||
@shared_access_required(EcoAccount, "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
|
||||
acc = get_object_or_404(EcoAccount, id=id)
|
||||
if acc.is_recorded:
|
||||
messages.info(
|
||||
request,
|
||||
RECORDED_BLOCKS_EDIT
|
||||
)
|
||||
return redirect("compensation:acc:detail", id=id)
|
||||
|
||||
# Create forms, initialize with values from db/from POST request
|
||||
data_form = EditEcoAccountForm(request.POST or None, instance=acc)
|
||||
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=acc)
|
||||
if request.method == "POST":
|
||||
data_form_valid = data_form.is_valid()
|
||||
geom_form_valid = geom_form.is_valid()
|
||||
if data_form_valid and geom_form_valid:
|
||||
# The data form takes the geom form for processing, as well as the performing user
|
||||
acc = data_form.save(request.user, geom_form)
|
||||
messages.success(request, _("Eco-Account {} edited").format(acc.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:acc:detail", id=acc.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(acc.identifier),
|
||||
}
|
||||
context = BaseContext(request, context).context
|
||||
return render(request, template, context)
|
||||
|
||||
|
||||
class EcoAccountDetailView(BaseDetailView):
|
||||
""" Detail view class
|
||||
|
||||
Renders details of an eco account
|
||||
|
||||
"""
|
||||
_MODEL_CLS = EcoAccount
|
||||
_TEMPLATE = "compensation/detail/eco_account/view.html"
|
||||
|
||||
@ -256,6 +150,11 @@ class EcoAccountDetailView(BaseDetailView):
|
||||
|
||||
|
||||
class RemoveEcoAccountView(LoginRequiredMixin, BaseRemoveModalFormView):
|
||||
""" Form view class
|
||||
|
||||
Renders a form for removing eco accounts
|
||||
|
||||
"""
|
||||
_MODEL_CLS = EcoAccount
|
||||
_FORM_CLS = RemoveEcoAccountModalForm
|
||||
_REDIRECT_URL = "compensation:acc:index"
|
||||
|
||||
@ -10,10 +10,10 @@ from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from compensation.forms.modals.payment import NewPaymentForm, RemovePaymentModalForm, EditPaymentModalForm
|
||||
from intervention.models import Intervention
|
||||
from konova.utils.message_templates import PAYMENT_ADDED, PAYMENT_REMOVED, PAYMENT_EDITED
|
||||
from konova.views.base import BaseModalFormView
|
||||
from konova.views.modal import AbstractModalFormView
|
||||
|
||||
|
||||
class BasePaymentView(LoginRequiredMixin, BaseModalFormView):
|
||||
class BasePaymentView(LoginRequiredMixin, AbstractModalFormView):
|
||||
_MODEL_CLS = Intervention
|
||||
_REDIRECT_URL = "intervention:detail"
|
||||
|
||||
|
||||
@ -12,13 +12,14 @@ from django.utils.translation import gettext_lazy as _
|
||||
from ema.forms import NewEmaForm, EditEmaForm
|
||||
from ema.models import Ema
|
||||
from ema.tables import EmaTable
|
||||
from konova.views.base import BaseIndexView, BaseIdentifierGeneratorView, BaseNewSpatialLocatedObjectFormView, \
|
||||
BaseEditSpatialLocatedObjectFormView
|
||||
from konova.views.identifier import AbstractIdentifierGeneratorView
|
||||
from konova.views.form import AbstractNewGeometryFormView, AbstractEditGeometryFormView
|
||||
from konova.views.index import AbstractIndexView
|
||||
from konova.views.detail import BaseDetailView
|
||||
from konova.views.remove import BaseRemoveModalFormView
|
||||
|
||||
|
||||
class EmaIndexView(LoginRequiredMixin, BaseIndexView):
|
||||
class EmaIndexView(LoginRequiredMixin, AbstractIndexView):
|
||||
_TAB_TITLE = _("EMAs - Overview")
|
||||
_INDEX_TABLE_CLS = EmaTable
|
||||
|
||||
@ -31,7 +32,7 @@ class EmaIndexView(LoginRequiredMixin, BaseIndexView):
|
||||
return qs
|
||||
|
||||
|
||||
class NewEmaFormView(BaseNewSpatialLocatedObjectFormView):
|
||||
class NewEmaFormView(AbstractNewGeometryFormView):
|
||||
_FORM_CLS = NewEmaForm
|
||||
_MODEL_CLS = Ema
|
||||
_TEMPLATE = "ema/form/view.html"
|
||||
@ -43,7 +44,7 @@ class NewEmaFormView(BaseNewSpatialLocatedObjectFormView):
|
||||
return user.is_ets_user()
|
||||
|
||||
|
||||
class EditEmaFormView(BaseEditSpatialLocatedObjectFormView):
|
||||
class EditEmaFormView(AbstractEditGeometryFormView):
|
||||
_MODEL_CLS = Ema
|
||||
_FORM_CLS = EditEmaForm
|
||||
_TEMPLATE = "ema/form/view.html"
|
||||
@ -55,7 +56,7 @@ class EditEmaFormView(BaseEditSpatialLocatedObjectFormView):
|
||||
return user.is_ets_user()
|
||||
|
||||
|
||||
class EmaIdentifierGeneratorView(LoginRequiredMixin, BaseIdentifierGeneratorView):
|
||||
class EmaIdentifierGeneratorView(LoginRequiredMixin, AbstractIdentifierGeneratorView):
|
||||
_MODEL_CLS = Ema
|
||||
_REDIRECT_URL = "ema:index"
|
||||
|
||||
|
||||
@ -10,10 +10,10 @@ from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from intervention.forms.modals.check import CheckModalForm
|
||||
from intervention.models import Intervention
|
||||
from konova.views.base import BaseModalFormView
|
||||
from konova.views.modal import AbstractModalFormView
|
||||
|
||||
|
||||
class InterventionCheckView(LoginRequiredMixin, BaseModalFormView):
|
||||
class InterventionCheckView(LoginRequiredMixin, AbstractModalFormView):
|
||||
_MODEL_CLS = Intervention
|
||||
_FORM_CLS = CheckModalForm
|
||||
_MSG_SUCCESS = _("Check performed")
|
||||
|
||||
@ -5,29 +5,22 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||
Created on: 19.08.22
|
||||
|
||||
"""
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.http import HttpRequest
|
||||
from django.shortcuts import get_object_or_404, render, redirect
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from intervention.forms.intervention import EditInterventionForm, NewInterventionForm
|
||||
from intervention.models import Intervention
|
||||
from intervention.tables import InterventionTable
|
||||
from konova.contexts import BaseContext
|
||||
from konova.decorators import default_group_required, shared_access_required
|
||||
from konova.forms import SimpleGeomForm
|
||||
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||
from konova.utils.message_templates import DATA_CHECKED_PREVIOUSLY_TEMPLATE, RECORDED_BLOCKS_EDIT, \
|
||||
CHECK_STATE_RESET, FORM_INVALID, GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE
|
||||
from konova.views.base import BaseIndexView, BaseIdentifierGeneratorView, BaseNewSpatialLocatedObjectFormView, \
|
||||
BaseEditSpatialLocatedObjectFormView
|
||||
from konova.utils.message_templates import DATA_CHECKED_PREVIOUSLY_TEMPLATE
|
||||
from konova.views.identifier import AbstractIdentifierGeneratorView
|
||||
from konova.views.form import AbstractNewGeometryFormView, AbstractEditGeometryFormView
|
||||
from konova.views.index import AbstractIndexView
|
||||
from konova.views.detail import BaseDetailView
|
||||
from konova.views.remove import BaseRemoveModalFormView
|
||||
|
||||
|
||||
class InterventionIndexView(LoginRequiredMixin, BaseIndexView):
|
||||
class InterventionIndexView(LoginRequiredMixin, AbstractIndexView):
|
||||
_INDEX_TABLE_CLS = InterventionTable
|
||||
_TAB_TITLE = _("Interventions - Overview")
|
||||
|
||||
@ -42,7 +35,7 @@ class InterventionIndexView(LoginRequiredMixin, BaseIndexView):
|
||||
return qs
|
||||
|
||||
|
||||
class NewInterventionFormView(BaseNewSpatialLocatedObjectFormView):
|
||||
class NewInterventionFormView(AbstractNewGeometryFormView):
|
||||
_MODEL_CLS = Intervention
|
||||
_FORM_CLS = NewInterventionForm
|
||||
_TEMPLATE = "intervention/form/view.html"
|
||||
@ -50,7 +43,7 @@ class NewInterventionFormView(BaseNewSpatialLocatedObjectFormView):
|
||||
_TAB_TITLE = _("New intervention")
|
||||
|
||||
|
||||
class EditInterventionFormView(BaseEditSpatialLocatedObjectFormView):
|
||||
class EditInterventionFormView(AbstractEditGeometryFormView):
|
||||
_MODEL_CLS = Intervention
|
||||
_FORM_CLS = EditInterventionForm
|
||||
_TEMPLATE = "intervention/form/view.html"
|
||||
@ -58,7 +51,7 @@ class EditInterventionFormView(BaseEditSpatialLocatedObjectFormView):
|
||||
_TAB_TITLE = _("Edit {}")
|
||||
|
||||
|
||||
class InterventionIdentifierGeneratorView(LoginRequiredMixin, BaseIdentifierGeneratorView):
|
||||
class InterventionIdentifierGeneratorView(LoginRequiredMixin, AbstractIdentifierGeneratorView):
|
||||
_MODEL_CLS = Intervention
|
||||
_REDIRECT_URL = "intervention:index"
|
||||
|
||||
@ -117,69 +110,6 @@ class InterventionDetailView(BaseDetailView):
|
||||
}
|
||||
return context
|
||||
|
||||
|
||||
@login_required
|
||||
@default_group_required
|
||||
@shared_access_required(Intervention, "id")
|
||||
def edit_view(request: HttpRequest, id: str):
|
||||
"""
|
||||
Renders a view for editing interventions
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
template = "intervention/form/view.html"
|
||||
# Get object from db
|
||||
intervention = get_object_or_404(Intervention, id=id)
|
||||
if intervention.is_recorded:
|
||||
messages.info(
|
||||
request,
|
||||
RECORDED_BLOCKS_EDIT
|
||||
)
|
||||
return redirect("intervention:detail", id=id)
|
||||
|
||||
# Create forms, initialize with values from db/from POST request
|
||||
data_form = EditInterventionForm(request.POST or None, instance=intervention)
|
||||
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=intervention)
|
||||
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
|
||||
# Save the current state of recorded|checked to inform the user in case of a status reset due to editing
|
||||
intervention_is_checked = intervention.checked is not None
|
||||
intervention = data_form.save(request.user, geom_form)
|
||||
messages.success(request, _("Intervention {} edited").format(intervention.identifier))
|
||||
if intervention_is_checked:
|
||||
messages.info(request, CHECK_STATE_RESET)
|
||||
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: _("Edit {}").format(intervention.identifier),
|
||||
}
|
||||
context = BaseContext(request, context).context
|
||||
return render(request, template, context)
|
||||
|
||||
class RemoveInterventionView(LoginRequiredMixin, BaseRemoveModalFormView):
|
||||
_MODEL_CLS = Intervention
|
||||
_REDIRECT_URL = "intervention:index"
|
||||
|
||||
@ -10,10 +10,10 @@ from django.urls import reverse
|
||||
from intervention.models import Intervention
|
||||
from konova.sub_settings.django_settings import BASE_URL
|
||||
from konova.utils.qrcode import QrCode
|
||||
from konova.views.report import BaseReportView
|
||||
from konova.views.report import AbstractReportView
|
||||
|
||||
|
||||
class InterventionReportView(BaseReportView):
|
||||
class InterventionReportView(AbstractReportView):
|
||||
_TEMPLATE = 'intervention/report/report.html'
|
||||
_MODEL = Intervention
|
||||
|
||||
|
||||
@ -15,10 +15,10 @@ from intervention.forms.modals.revocation import NewRevocationModalForm, EditRev
|
||||
from intervention.models import Intervention, RevocationDocument
|
||||
from konova.utils.documents import get_document
|
||||
from konova.utils.message_templates import DATA_UNSHARED, REVOCATION_EDITED, REVOCATION_REMOVED, REVOCATION_ADDED
|
||||
from konova.views.base import BaseModalFormView, BaseView
|
||||
from konova.views.modal import AbstractModalFormView, AbstractBaseView
|
||||
|
||||
|
||||
class BaseRevocationView(LoginRequiredMixin, BaseModalFormView):
|
||||
class BaseRevocationView(LoginRequiredMixin, AbstractModalFormView):
|
||||
_MODEL_CLS = Intervention
|
||||
_REDIRECT_URL = "intervention:detail"
|
||||
|
||||
@ -48,7 +48,7 @@ class RemoveRevocationView(BaseRevocationView):
|
||||
_MSG_SUCCESS = REVOCATION_REMOVED
|
||||
|
||||
|
||||
class GetRevocationDocumentView(LoginRequiredMixin, BaseView):
|
||||
class GetRevocationDocumentView(LoginRequiredMixin, AbstractBaseView):
|
||||
_MODEL_CLS = RevocationDocument
|
||||
_REDIRECT_URL = "intervention:detail"
|
||||
|
||||
|
||||
@ -11,10 +11,10 @@ from compensation.forms.modals.compensation_action import NewCompensationActionM
|
||||
EditCompensationActionModalForm, RemoveCompensationActionModalForm
|
||||
from konova.utils.message_templates import COMPENSATION_STATE_ADDED, COMPENSATION_STATE_EDITED, \
|
||||
COMPENSATION_STATE_REMOVED
|
||||
from konova.views.base import BaseModalFormView
|
||||
from konova.views.modal import AbstractModalFormView
|
||||
|
||||
|
||||
class AbstractCompensationActionView(LoginRequiredMixin, BaseModalFormView):
|
||||
class AbstractCompensationActionView(LoginRequiredMixin, AbstractModalFormView):
|
||||
_MODEL_CLS = None
|
||||
_REDIRECT_URL = None
|
||||
|
||||
|
||||
@ -5,26 +5,16 @@ Created on: 15.10.25
|
||||
"""
|
||||
from abc import abstractmethod
|
||||
|
||||
from bootstrap_modal_forms.mixins import is_ajax
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.http import HttpRequest, JsonResponse, HttpResponseRedirect
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.http import HttpRequest
|
||||
from django.shortcuts import redirect
|
||||
from django.urls import reverse
|
||||
from django.views import View
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from konova.contexts import BaseContext
|
||||
from konova.forms import BaseForm, SimpleGeomForm
|
||||
from konova.models import BaseObject
|
||||
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||
from konova.utils.general import check_user_is_in_any_group
|
||||
from konova.utils.message_templates import MISSING_GROUP_PERMISSION, DATA_UNSHARED, IDENTIFIER_REPLACED, \
|
||||
GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE, RECORDED_BLOCKS_EDIT, FORM_INVALID
|
||||
from konova.utils.message_templates import MISSING_GROUP_PERMISSION, DATA_UNSHARED
|
||||
|
||||
|
||||
class BaseView(View):
|
||||
class AbstractBaseView(View):
|
||||
""" An abstract base view
|
||||
|
||||
This class represents the root of all views on this project. It defines private variables which have to be used
|
||||
@ -123,519 +113,3 @@ class BaseView(View):
|
||||
"""
|
||||
return self._REDIRECT_URL_ERROR
|
||||
|
||||
class BaseModalFormView(BaseView):
|
||||
""" Abstract base view providing logic to perform most modal form based view renderings
|
||||
|
||||
"""
|
||||
_TEMPLATE: str = "modal/modal_form.html"
|
||||
_MODEL_CLS = None
|
||||
_FORM_CLS = None
|
||||
_MSG_SUCCESS = None
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def _user_has_shared_access(self, user, **kwargs):
|
||||
""" Checks whether the user has shared access to this object.
|
||||
|
||||
For objects inheriting from BaseObject class the method 'is_shared_with()' is a handy
|
||||
wrapper for checking shared access. For any other circumstances this method should be overwritten
|
||||
to provide custom shared-access-checking logic.
|
||||
|
||||
If no shared-access-check is needed, this method can be overwritten with a simple True returning.
|
||||
|
||||
Args:
|
||||
user (User): The performing user
|
||||
**kwargs ():
|
||||
|
||||
Returns:
|
||||
has_shared_access (bool): Whether the user has shared access
|
||||
"""
|
||||
obj = get_object_or_404(self._MODEL_CLS, id=kwargs.get("id"))
|
||||
return obj.is_shared_with(user)
|
||||
|
||||
def get(self, request: HttpRequest, *args, **kwargs):
|
||||
""" GET endpoint for rendering a view holding a modal form
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
*args ():
|
||||
**kwargs ():
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
# If there is an id provided as mapped parameter from the URL take it ...
|
||||
_id = kwargs.pop("id", None)
|
||||
try:
|
||||
# ... and try to resolve it into a record
|
||||
obj = self._MODEL_CLS.objects.get(id=_id)
|
||||
self._check_for_recorded_instance(obj)
|
||||
except ObjectDoesNotExist:
|
||||
# ... If there is none, maybe we are currently processing
|
||||
# the creation of a new object (therefore no id yet), so let's continue
|
||||
obj = None
|
||||
form = self._FORM_CLS(
|
||||
request.POST or None,
|
||||
request.FILES or None,
|
||||
instance=obj,
|
||||
request=request,
|
||||
**kwargs
|
||||
)
|
||||
context = {
|
||||
"form": form,
|
||||
}
|
||||
context = BaseContext(request, context).context
|
||||
return render(request, self._TEMPLATE, context)
|
||||
|
||||
def post(self, request: HttpRequest, *args, **kwargs):
|
||||
""" POST endpoint for processing form contents of a view
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
*args ():
|
||||
**kwargs ():
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
# If there is an id provided as mapped parameter from the URL take it ...
|
||||
_id = kwargs.pop("id", None)
|
||||
try:
|
||||
# ... and try to resolve it into a record
|
||||
obj = self._MODEL_CLS.objects.get(id=_id)
|
||||
self._check_for_recorded_instance(obj)
|
||||
except ObjectDoesNotExist:
|
||||
# ... If there is none, maybe we are currently processing
|
||||
# the creation of a new object (therefore no id yet), so let's continue
|
||||
obj = None
|
||||
form = self._FORM_CLS(
|
||||
request.POST or None,
|
||||
request.FILES or None,
|
||||
instance=obj,
|
||||
request=request,
|
||||
**kwargs
|
||||
)
|
||||
# Get now the redirect url and take specifics of the obj into account for that.
|
||||
# We do not do this after saving the form to avoid side effects due to possibly changed data
|
||||
redirect_url = self._get_redirect_url(obj=obj)
|
||||
if form.is_valid():
|
||||
# Modal forms send one POST for checking on data validity. This is used to evaluate possible errors
|
||||
# on the form. The second POST (if no errors have been found) is the 'proper' one,
|
||||
# which we want to process by saving/commiting of the data to the database.
|
||||
if not is_ajax(request.META):
|
||||
# Get now the success message and take specifics of the obj into account for that
|
||||
msg_success = self._get_msg_success(obj=obj, *args, **kwargs)
|
||||
form.save()
|
||||
messages.success(
|
||||
request,
|
||||
msg_success
|
||||
)
|
||||
return HttpResponseRedirect(redirect_url)
|
||||
else:
|
||||
context = {
|
||||
"form": form,
|
||||
}
|
||||
context = BaseContext(request, context).context
|
||||
return render(request, self._TEMPLATE, context)
|
||||
|
||||
def _get_redirect_url(self, *args, **kwargs):
|
||||
""" Getter to construct a more specific, data dependant redirect URL (if needed)
|
||||
|
||||
Args:
|
||||
*args ():
|
||||
**kwargs ():
|
||||
|
||||
Returns:
|
||||
url (str): Reversed redirect url
|
||||
"""
|
||||
obj = kwargs.get("obj", None)
|
||||
if obj:
|
||||
return reverse(self._REDIRECT_URL, args=(obj.id,))
|
||||
else:
|
||||
return reverse(self._REDIRECT_URL)
|
||||
|
||||
def _get_msg_success(self, *args, **kwargs):
|
||||
""" Getter to construct a more specific, data dependant success message
|
||||
|
||||
Args:
|
||||
*args ():
|
||||
**kwargs ():
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return self._MSG_SUCCESS
|
||||
|
||||
def _check_for_recorded_instance(self, obj):
|
||||
""" Checks if the object on this view is recorded and runs some special logic if so
|
||||
|
||||
If the instance is recorded, the view should provide some information about why the user can not edit anything.
|
||||
This behaviour is only intended to mask any form for instances based on the BaseObject class.
|
||||
|
||||
There are situations where the form should be rendered regularly, despite the instance being recorded,
|
||||
e.g. for rendering deduction form contents on (recorded) eco accounts.
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
is_none = obj is None
|
||||
is_other_data_type = not isinstance(obj, BaseObject)
|
||||
|
||||
if is_none or is_other_data_type:
|
||||
# Do nothing
|
||||
return
|
||||
|
||||
if obj.is_recorded:
|
||||
# Replace default template with a blocking one
|
||||
self._TEMPLATE = "form/recorded_no_edit.html"
|
||||
|
||||
|
||||
class BaseIndexView(BaseView):
|
||||
""" Abstract base class for index views
|
||||
|
||||
"""
|
||||
_TEMPLATE: str = "generic_index.html"
|
||||
_INDEX_TABLE_CLS = None
|
||||
_REDIRECT_URL: str = "home"
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def get(self, request: HttpRequest, *args, **kwargs):
|
||||
""" GET endpoint for rendering index views
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
*args ():
|
||||
**kwargs ():
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
qs = self._get_queryset()
|
||||
table = self._INDEX_TABLE_CLS(
|
||||
request=request,
|
||||
queryset=qs
|
||||
)
|
||||
context = {
|
||||
"table": table,
|
||||
TAB_TITLE_IDENTIFIER: self._TAB_TITLE,
|
||||
}
|
||||
context = BaseContext(request, context).context
|
||||
return render(request, self._TEMPLATE, context)
|
||||
|
||||
@abstractmethod
|
||||
def _get_queryset(self):
|
||||
""" Generic getter for the queryset of objects which shall be processed on this view
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
# No specific permissions needed for opening base index view
|
||||
return True
|
||||
|
||||
def _user_has_shared_access(self, user, **kwargs):
|
||||
# No specific constraints for shared access of index views
|
||||
return True
|
||||
|
||||
|
||||
class BaseIdentifierGeneratorView(BaseView):
|
||||
_MODEL_CLS = None
|
||||
_REDIRECT_URL: str = "home"
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def get(self, request: HttpRequest):
|
||||
tmp_obj = self._MODEL_CLS()
|
||||
identifier = tmp_obj.generate_new_identifier()
|
||||
while self._MODEL_CLS.objects.filter(identifier=identifier).exists():
|
||||
identifier = tmp_obj.generate_new_identifier()
|
||||
return JsonResponse(
|
||||
data={
|
||||
"gen_data": identifier
|
||||
}
|
||||
)
|
||||
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
""" Should be overwritten in inheriting classes!
|
||||
|
||||
Args:
|
||||
user ():
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return user.is_default_user()
|
||||
|
||||
def _user_has_shared_access(self, user, **kwargs):
|
||||
# No specific constraints for shared access
|
||||
return True
|
||||
|
||||
|
||||
class BaseFormView(BaseView):
|
||||
""" Abstract base class for rendering form views
|
||||
|
||||
"""
|
||||
_MODEL_CLS = None
|
||||
_FORM_CLS = None
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def _get_additional_context(self, **kwargs):
|
||||
""" Getter for additional data, which is needed to properly render the current view
|
||||
|
||||
Args:
|
||||
**kwargs ():
|
||||
|
||||
Returns:
|
||||
context (dict): Additional context data for rendering
|
||||
"""
|
||||
return {}
|
||||
|
||||
|
||||
class BaseSpatialLocatedObjectFormView(LoginRequiredMixin, BaseFormView):
|
||||
""" Abstract base view for processing objects with spatial data
|
||||
|
||||
"""
|
||||
_GEOMETRY_FORM_CLS = SimpleGeomForm
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
||||
class BaseNewSpatialLocatedObjectFormView(BaseSpatialLocatedObjectFormView):
|
||||
""" Base view for creating new spatial data related to objects
|
||||
|
||||
"""
|
||||
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
# 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, *args, **kwargs):
|
||||
""" GET endpoint for rendering a form view where object data and spatial data are processed
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
**kwargs ():
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
# First initialize the regular object form and the geometry form based on request-bound data
|
||||
form: BaseForm = self._FORM_CLS(None, **kwargs, user=request.user)
|
||||
geom_form: SimpleGeomForm = self._GEOMETRY_FORM_CLS(None, user=request.user, read_only=False)
|
||||
|
||||
# Get some additional context and put everything into the rendering pipeline
|
||||
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, *args, **kwargs):
|
||||
""" POST endpoint for processing object and spatial data provided by forms
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
**kwargs ():
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
# First initialize the regular object form and the geometry form based on request-bound data
|
||||
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)
|
||||
|
||||
# Only continue if both forms are without errors
|
||||
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)
|
||||
|
||||
# There is a rare chance that an identifier has been taken already between sending the form and processing
|
||||
# the data. If the identifier can not be used anymore, we have to inform the user that another identifier
|
||||
# had to be generated
|
||||
if generated_identifier != obj.identifier:
|
||||
messages.info(
|
||||
request,
|
||||
IDENTIFIER_REPLACED.format(
|
||||
generated_identifier,
|
||||
obj.identifier
|
||||
)
|
||||
)
|
||||
messages.success(request, _("{} added").format(obj.identifier))
|
||||
# Very complex geometries have to be simplified automatically while processing the spatial data. If this
|
||||
# is the case, the user has to be informed. (They might want to check whether the stored geometry still
|
||||
# fits their needs)
|
||||
if geom_form.has_geometry_simplified():
|
||||
messages.info(
|
||||
request,
|
||||
GEOMETRY_SIMPLIFIED
|
||||
)
|
||||
|
||||
# If certain parts of the geometry do not pass the quality check (e.g. way too small and therefore more like
|
||||
# cutting errors) we need to inform the user that some parts have been removed/ignored while storing the
|
||||
# geometry
|
||||
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:
|
||||
# Something was not properly entered on the forms, so we have to inform the user
|
||||
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):
|
||||
""" Base view for editing new spatial data related to objects
|
||||
|
||||
"""
|
||||
_TAB_TITLE = _("Edit {}")
|
||||
|
||||
def get(self, request: HttpRequest, id: str, *args, **kwargs):
|
||||
""" GET endpoint for rendering a form view where object data and spatial data are processed
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
id (str): The id of the object (not the geometry)
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
# First fetch the object identified by the id
|
||||
obj = get_object_or_404(
|
||||
self._MODEL_CLS,
|
||||
id=id
|
||||
)
|
||||
obj_redirect_url = reverse(self._REDIRECT_URL, args=(obj.id,))
|
||||
|
||||
# Check whether the object is recorded. If so - we can redirect the user and inform about the un-editability
|
||||
# of this entry
|
||||
if obj.is_recorded:
|
||||
messages.info(
|
||||
request,
|
||||
RECORDED_BLOCKS_EDIT
|
||||
)
|
||||
return redirect(obj_redirect_url)
|
||||
|
||||
# Seems like the object is not recorded. Good - initialize the forms based on the obj and request-bound data
|
||||
form: BaseForm = self._FORM_CLS(None, instance=obj, user=request.user)
|
||||
geom_form: SimpleGeomForm = self._GEOMETRY_FORM_CLS(None, instance=obj, read_only=False)
|
||||
|
||||
# Get additional context for rendering and put everything in the rendering pipeline
|
||||
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, *args, **kwargs):
|
||||
""" POST endpoint for processing object and spatial data provided by forms
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
id (str): The object's id
|
||||
*args ():
|
||||
**kwargs ():
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
obj = get_object_or_404(
|
||||
self._MODEL_CLS,
|
||||
id=id
|
||||
)
|
||||
obj_redirect_url = reverse(self._REDIRECT_URL, args=(obj.id,))
|
||||
|
||||
# If the object is recorded, we abort the processing directly and inform the user
|
||||
if obj.is_recorded:
|
||||
messages.info(
|
||||
request,
|
||||
RECORDED_BLOCKS_EDIT
|
||||
)
|
||||
return redirect(obj_redirect_url)
|
||||
|
||||
# Initialize forms with obj and request-bound data
|
||||
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))
|
||||
|
||||
# Very complex geometries have to be simplified automatically while processing the spatial data. If this
|
||||
# is the case, the user has to be informed. (They might want to check whether the stored geometry still
|
||||
# fits their needs)
|
||||
if geom_form.has_geometry_simplified():
|
||||
messages.info(
|
||||
request,
|
||||
GEOMETRY_SIMPLIFIED
|
||||
)
|
||||
|
||||
# If certain parts of the geometry do not pass the quality check (e.g. way too small and therefore more like
|
||||
# cutting errors) we need to inform the user that some parts have been removed/ignored while storing the
|
||||
# geometry
|
||||
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, **kwargs):
|
||||
return user.is_default_user()
|
||||
|
||||
@ -10,10 +10,10 @@ from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from compensation.forms.modals.deadline import NewDeadlineModalForm, EditDeadlineModalForm
|
||||
from konova.forms.modals import RemoveDeadlineModalForm
|
||||
from konova.utils.message_templates import DEADLINE_ADDED, DEADLINE_EDITED, DEADLINE_REMOVED
|
||||
from konova.views.base import BaseModalFormView
|
||||
from konova.views.modal import AbstractModalFormView
|
||||
|
||||
|
||||
class AbstractNewDeadlineView(LoginRequiredMixin, BaseModalFormView):
|
||||
class AbstractNewDeadlineView(LoginRequiredMixin, AbstractModalFormView):
|
||||
_MODEL_CLS = None
|
||||
_FORM_CLS = NewDeadlineModalForm
|
||||
_REDIRECT_URL = None
|
||||
@ -29,7 +29,7 @@ class AbstractNewDeadlineView(LoginRequiredMixin, BaseModalFormView):
|
||||
return user.is_default_user()
|
||||
|
||||
|
||||
class AbstractEditDeadlineView(LoginRequiredMixin, BaseModalFormView):
|
||||
class AbstractEditDeadlineView(LoginRequiredMixin, AbstractModalFormView):
|
||||
_MODEL_CLS = None
|
||||
_FORM_CLS = EditDeadlineModalForm
|
||||
_REDIRECT_URL = None
|
||||
@ -45,7 +45,7 @@ class AbstractEditDeadlineView(LoginRequiredMixin, BaseModalFormView):
|
||||
return user.is_default_user()
|
||||
|
||||
|
||||
class AbstractRemoveDeadlineView(LoginRequiredMixin, BaseModalFormView):
|
||||
class AbstractRemoveDeadlineView(LoginRequiredMixin, AbstractModalFormView):
|
||||
_MODEL_CLS = None
|
||||
_FORM_CLS = RemoveDeadlineModalForm
|
||||
_REDIRECT_URL = None
|
||||
|
||||
@ -11,10 +11,10 @@ from django.urls import reverse
|
||||
from intervention.forms.modals.deduction import NewEcoAccountDeductionModalForm, EditEcoAccountDeductionModalForm, \
|
||||
RemoveEcoAccountDeductionModalForm
|
||||
from konova.utils.general import check_id_is_valid_uuid
|
||||
from konova.views.base import BaseModalFormView
|
||||
from konova.views.modal import AbstractModalFormView
|
||||
|
||||
|
||||
class AbstractDeductionView(BaseModalFormView):
|
||||
class AbstractDeductionView(AbstractModalFormView):
|
||||
_REDIRECT_URL = None
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
|
||||
@ -16,10 +16,10 @@ from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
|
||||
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||
from konova.utils.general import check_id_is_valid_uuid
|
||||
from konova.utils.message_templates import DO_NOT_FORGET_TO_SHARE
|
||||
from konova.views.base import BaseView
|
||||
from konova.views.base import AbstractBaseView
|
||||
|
||||
|
||||
class BaseDetailView(LoginRequiredMixin, BaseView):
|
||||
class BaseDetailView(LoginRequiredMixin, AbstractBaseView):
|
||||
_MODEL_CLS = None
|
||||
|
||||
class Meta:
|
||||
|
||||
@ -12,10 +12,10 @@ from django.shortcuts import get_object_or_404
|
||||
from konova.forms.modals import EditDocumentModalForm
|
||||
from konova.utils.documents import get_document
|
||||
from konova.utils.message_templates import DOCUMENT_ADDED, DOCUMENT_EDITED, DOCUMENT_REMOVED_TEMPLATE
|
||||
from konova.views.base import BaseModalFormView, BaseView
|
||||
from konova.views.modal import AbstractModalFormView, AbstractBaseView
|
||||
|
||||
|
||||
class AbstractNewDocumentView(LoginRequiredMixin, BaseModalFormView):
|
||||
class AbstractNewDocumentView(LoginRequiredMixin, AbstractModalFormView):
|
||||
_MODEL_CLS = None
|
||||
_FORM_CLS = None
|
||||
_REDIRECT_URL = None
|
||||
@ -31,7 +31,7 @@ class AbstractNewDocumentView(LoginRequiredMixin, BaseModalFormView):
|
||||
return user.is_default_user()
|
||||
|
||||
|
||||
class AbstractGetDocumentView(LoginRequiredMixin, BaseView):
|
||||
class AbstractGetDocumentView(LoginRequiredMixin, AbstractBaseView):
|
||||
_MODEL_CLS = None
|
||||
_DOCUMENT_CLS = None
|
||||
|
||||
@ -68,7 +68,7 @@ class AbstractGetDocumentView(LoginRequiredMixin, BaseView):
|
||||
return obj.is_shared_with(user)
|
||||
|
||||
|
||||
class AbstractRemoveDocumentView(LoginRequiredMixin, BaseModalFormView):
|
||||
class AbstractRemoveDocumentView(LoginRequiredMixin, AbstractModalFormView):
|
||||
_MODEL_CLS = None
|
||||
_DOCUMENT_CLS = None
|
||||
_FORM_CLS = None
|
||||
@ -90,7 +90,7 @@ class AbstractRemoveDocumentView(LoginRequiredMixin, BaseModalFormView):
|
||||
return self._MSG_SUCCESS.format(doc.title)
|
||||
|
||||
|
||||
class AbstractEditDocumentView(LoginRequiredMixin, BaseModalFormView):
|
||||
class AbstractEditDocumentView(LoginRequiredMixin, AbstractModalFormView):
|
||||
_MODEL_CLS = None
|
||||
_DOCUMENT_CLS = None
|
||||
_FORM_CLS = EditDocumentModalForm
|
||||
|
||||
282
konova/views/form.py
Normal file
282
konova/views/form.py
Normal file
@ -0,0 +1,282 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Created on: 12.12.25
|
||||
|
||||
"""
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.http import HttpRequest
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from konova.contexts import BaseContext
|
||||
from konova.forms import BaseForm, SimpleGeomForm
|
||||
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||
from konova.utils.message_templates import RECORDED_BLOCKS_EDIT, GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE, \
|
||||
FORM_INVALID, IDENTIFIER_REPLACED
|
||||
from konova.views.base import AbstractBaseView
|
||||
|
||||
|
||||
class AbstractFormView(AbstractBaseView):
|
||||
""" Abstract base class for rendering form views
|
||||
|
||||
"""
|
||||
_MODEL_CLS = None
|
||||
_FORM_CLS = None
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def _get_additional_context(self, **kwargs):
|
||||
""" Getter for additional data, which is needed to properly render the current view
|
||||
|
||||
Args:
|
||||
**kwargs ():
|
||||
|
||||
Returns:
|
||||
context (dict): Additional context data for rendering
|
||||
"""
|
||||
return {}
|
||||
|
||||
|
||||
class AbstractGeometryFormView(LoginRequiredMixin, AbstractFormView):
|
||||
""" Abstract base view for processing objects with spatial data
|
||||
|
||||
"""
|
||||
_GEOMETRY_FORM_CLS = SimpleGeomForm
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
||||
class AbstractNewGeometryFormView(AbstractGeometryFormView):
|
||||
""" Base view for creating new spatial data related to objects
|
||||
|
||||
"""
|
||||
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
# 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, *args, **kwargs):
|
||||
""" GET endpoint for rendering a form view where object data and spatial data are processed
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
**kwargs ():
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
# First initialize the regular object form and the geometry form based on request-bound data
|
||||
form: BaseForm = self._FORM_CLS(None, **kwargs, user=request.user)
|
||||
geom_form: SimpleGeomForm = self._GEOMETRY_FORM_CLS(None, user=request.user, read_only=False)
|
||||
|
||||
# Get some additional context and put everything into the rendering pipeline
|
||||
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, *args, **kwargs):
|
||||
""" POST endpoint for processing object and spatial data provided by forms
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
**kwargs ():
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
# First initialize the regular object form and the geometry form based on request-bound data
|
||||
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)
|
||||
|
||||
# Only continue if both forms are without errors
|
||||
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)
|
||||
|
||||
# There is a rare chance that an identifier has been taken already between sending the form and processing
|
||||
# the data. If the identifier can not be used anymore, we have to inform the user that another identifier
|
||||
# had to be generated
|
||||
if generated_identifier != obj.identifier:
|
||||
messages.info(
|
||||
request,
|
||||
IDENTIFIER_REPLACED.format(
|
||||
generated_identifier,
|
||||
obj.identifier
|
||||
)
|
||||
)
|
||||
messages.success(request, _("{} added").format(obj.identifier))
|
||||
# Very complex geometries have to be simplified automatically while processing the spatial data. If this
|
||||
# is the case, the user has to be informed. (They might want to check whether the stored geometry still
|
||||
# fits their needs)
|
||||
if geom_form.has_geometry_simplified():
|
||||
messages.info(
|
||||
request,
|
||||
GEOMETRY_SIMPLIFIED
|
||||
)
|
||||
|
||||
# If certain parts of the geometry do not pass the quality check (e.g. way too small and therefore more like
|
||||
# cutting errors) we need to inform the user that some parts have been removed/ignored while storing the
|
||||
# geometry
|
||||
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:
|
||||
# Something was not properly entered on the forms, so we have to inform the user
|
||||
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 AbstractEditGeometryFormView(AbstractGeometryFormView):
|
||||
""" Base view for editing new spatial data related to objects
|
||||
|
||||
"""
|
||||
_TAB_TITLE = _("Edit {}")
|
||||
|
||||
def get(self, request: HttpRequest, id: str, *args, **kwargs):
|
||||
""" GET endpoint for rendering a form view where object data and spatial data are processed
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
id (str): The id of the object (not the geometry)
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
# First fetch the object identified by the id
|
||||
obj = get_object_or_404(
|
||||
self._MODEL_CLS,
|
||||
id=id
|
||||
)
|
||||
obj_redirect_url = reverse(self._REDIRECT_URL, args=(obj.id,))
|
||||
|
||||
# Check whether the object is recorded. If so - we can redirect the user and inform about the un-editability
|
||||
# of this entry
|
||||
if obj.is_recorded:
|
||||
messages.info(
|
||||
request,
|
||||
RECORDED_BLOCKS_EDIT
|
||||
)
|
||||
return redirect(obj_redirect_url)
|
||||
|
||||
# Seems like the object is not recorded. Good - initialize the forms based on the obj and request-bound data
|
||||
form: BaseForm = self._FORM_CLS(None, instance=obj, user=request.user)
|
||||
geom_form: SimpleGeomForm = self._GEOMETRY_FORM_CLS(None, instance=obj, read_only=False)
|
||||
|
||||
# Get additional context for rendering and put everything in the rendering pipeline
|
||||
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, *args, **kwargs):
|
||||
""" POST endpoint for processing object and spatial data provided by forms
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
id (str): The object's id
|
||||
*args ():
|
||||
**kwargs ():
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
obj = get_object_or_404(
|
||||
self._MODEL_CLS,
|
||||
id=id
|
||||
)
|
||||
obj_redirect_url = reverse(self._REDIRECT_URL, args=(obj.id,))
|
||||
|
||||
# If the object is recorded, we abort the processing directly and inform the user
|
||||
if obj.is_recorded:
|
||||
messages.info(
|
||||
request,
|
||||
RECORDED_BLOCKS_EDIT
|
||||
)
|
||||
return redirect(obj_redirect_url)
|
||||
|
||||
# Initialize forms with obj and request-bound data
|
||||
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))
|
||||
|
||||
# Very complex geometries have to be simplified automatically while processing the spatial data. If this
|
||||
# is the case, the user has to be informed. (They might want to check whether the stored geometry still
|
||||
# fits their needs)
|
||||
if geom_form.has_geometry_simplified():
|
||||
messages.info(
|
||||
request,
|
||||
GEOMETRY_SIMPLIFIED
|
||||
)
|
||||
|
||||
# If certain parts of the geometry do not pass the quality check (e.g. way too small and therefore more like
|
||||
# cutting errors) we need to inform the user that some parts have been removed/ignored while storing the
|
||||
# geometry
|
||||
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, **kwargs):
|
||||
return user.is_default_user()
|
||||
@ -15,10 +15,10 @@ from konova.models import Geometry
|
||||
from konova.settings import GEOM_THRESHOLD_RECALCULATION_SECONDS
|
||||
from konova.sub_settings.lanis_settings import DEFAULT_SRID_RLP
|
||||
from konova.tasks import celery_update_parcels
|
||||
from konova.views.base import BaseView
|
||||
from konova.views.base import AbstractBaseView
|
||||
|
||||
|
||||
class GeomParcelsView(BaseView):
|
||||
class GeomParcelsView(AbstractBaseView):
|
||||
_TEMPLATE = "konova/includes/parcels/parcel_table_frame.html"
|
||||
|
||||
def get(self, request: HttpRequest, id: str):
|
||||
@ -114,7 +114,7 @@ class GeomParcelsView(BaseView):
|
||||
return True
|
||||
|
||||
|
||||
class GeomParcelsContentView(BaseView):
|
||||
class GeomParcelsContentView(AbstractBaseView):
|
||||
_TEMPLATE = "konova/includes/parcels/parcel_table_content.html"
|
||||
|
||||
def get(self, request: HttpRequest, id: str, page: int):
|
||||
|
||||
@ -15,11 +15,11 @@ from compensation.models import EcoAccount, Compensation
|
||||
from intervention.models import Intervention
|
||||
from konova.contexts import BaseContext
|
||||
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||
from konova.views.base import BaseView
|
||||
from konova.views.base import AbstractBaseView
|
||||
from news.models import ServerMessage
|
||||
|
||||
|
||||
class HomeView(LoginRequiredMixin, BaseView):
|
||||
class HomeView(LoginRequiredMixin, AbstractBaseView):
|
||||
_TEMPLATE = "konova/home.html"
|
||||
|
||||
def get(self, request: HttpRequest):
|
||||
|
||||
55
konova/views/identifier.py
Normal file
55
konova/views/identifier.py
Normal file
@ -0,0 +1,55 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Created on: 12.12.25
|
||||
|
||||
"""
|
||||
from django.http import HttpRequest, JsonResponse
|
||||
|
||||
from konova.views.base import AbstractBaseView
|
||||
|
||||
|
||||
class AbstractIdentifierGeneratorView(AbstractBaseView):
|
||||
""" View class
|
||||
|
||||
Process a request for generating a new identifier
|
||||
|
||||
"""
|
||||
_MODEL_CLS = None
|
||||
_REDIRECT_URL: str = "home"
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def get(self, request: HttpRequest):
|
||||
""" GET endpoint
|
||||
|
||||
Args:
|
||||
request ():
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
tmp_obj = self._MODEL_CLS()
|
||||
identifier = tmp_obj.generate_new_identifier()
|
||||
while self._MODEL_CLS.objects.filter(identifier=identifier).exists():
|
||||
identifier = tmp_obj.generate_new_identifier()
|
||||
return JsonResponse(
|
||||
data={
|
||||
"gen_data": identifier
|
||||
}
|
||||
)
|
||||
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
""" Should be overwritten in inheriting classes!
|
||||
|
||||
Args:
|
||||
user ():
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return user.is_default_user()
|
||||
|
||||
def _user_has_shared_access(self, user, **kwargs):
|
||||
# No specific constraints for shared access
|
||||
return True
|
||||
65
konova/views/index.py
Normal file
65
konova/views/index.py
Normal file
@ -0,0 +1,65 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Created on: 12.12.25
|
||||
|
||||
"""
|
||||
from abc import abstractmethod
|
||||
|
||||
from django.http import HttpRequest
|
||||
from django.shortcuts import render
|
||||
|
||||
from konova.contexts import BaseContext
|
||||
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||
from konova.views.base import AbstractBaseView
|
||||
|
||||
|
||||
class AbstractIndexView(AbstractBaseView):
|
||||
""" Abstract base class for all index views
|
||||
|
||||
"""
|
||||
_TEMPLATE: str = "generic_index.html"
|
||||
_INDEX_TABLE_CLS = None
|
||||
_REDIRECT_URL: str = "home"
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def get(self, request: HttpRequest, *args, **kwargs):
|
||||
""" GET endpoint for rendering index views
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
*args ():
|
||||
**kwargs ():
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
qs = self._get_queryset()
|
||||
table = self._INDEX_TABLE_CLS(
|
||||
request=request,
|
||||
queryset=qs
|
||||
)
|
||||
context = {
|
||||
"table": table,
|
||||
TAB_TITLE_IDENTIFIER: self._TAB_TITLE,
|
||||
}
|
||||
context = BaseContext(request, context).context
|
||||
return render(request, self._TEMPLATE, context)
|
||||
|
||||
@abstractmethod
|
||||
def _get_queryset(self):
|
||||
""" Generic getter for the queryset of objects which shall be processed on this view
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
# No specific permissions needed for opening base index view
|
||||
return True
|
||||
|
||||
def _user_has_shared_access(self, user, **kwargs):
|
||||
# No specific constraints for shared access of index views
|
||||
return True
|
||||
@ -9,10 +9,10 @@ from django.shortcuts import get_object_or_404, render
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from konova.contexts import BaseContext
|
||||
from konova.views.base import BaseView
|
||||
from konova.views.base import AbstractBaseView
|
||||
|
||||
|
||||
class AbstractLogView(BaseView):
|
||||
class AbstractLogView(AbstractBaseView):
|
||||
_MODEL_CLS = None
|
||||
_TEMPLATE = "modal/modal_generic.html"
|
||||
|
||||
|
||||
183
konova/views/modal.py
Normal file
183
konova/views/modal.py
Normal file
@ -0,0 +1,183 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Created on: 12.12.25
|
||||
|
||||
"""
|
||||
from bootstrap_modal_forms.mixins import is_ajax
|
||||
from django.contrib import messages
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.http import HttpRequest, HttpResponseRedirect
|
||||
from django.shortcuts import get_object_or_404, render
|
||||
from django.urls import reverse
|
||||
|
||||
from konova.contexts import BaseContext
|
||||
from konova.models import BaseObject
|
||||
from konova.views.base import AbstractBaseView
|
||||
|
||||
|
||||
class AbstractModalFormView(AbstractBaseView):
|
||||
""" Abstract base view providing logic to perform most modal form based view renderings
|
||||
|
||||
"""
|
||||
_TEMPLATE: str = "modal/modal_form.html"
|
||||
_MODEL_CLS = None
|
||||
_FORM_CLS = None
|
||||
_MSG_SUCCESS = None
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def _user_has_shared_access(self, user, **kwargs):
|
||||
""" Checks whether the user has shared access to this object.
|
||||
|
||||
For objects inheriting from BaseObject class the method 'is_shared_with()' is a handy
|
||||
wrapper for checking shared access. For any other circumstances this method should be overwritten
|
||||
to provide custom shared-access-checking logic.
|
||||
|
||||
If no shared-access-check is needed, this method can be overwritten with a simple True returning.
|
||||
|
||||
Args:
|
||||
user (User): The performing user
|
||||
**kwargs ():
|
||||
|
||||
Returns:
|
||||
has_shared_access (bool): Whether the user has shared access
|
||||
"""
|
||||
obj = get_object_or_404(self._MODEL_CLS, id=kwargs.get("id"))
|
||||
return obj.is_shared_with(user)
|
||||
|
||||
def get(self, request: HttpRequest, *args, **kwargs):
|
||||
""" GET endpoint for rendering a view holding a modal form
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
*args ():
|
||||
**kwargs ():
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
# If there is an id provided as mapped parameter from the URL -> take it ...
|
||||
_id = kwargs.pop("id", None)
|
||||
try:
|
||||
# ... and try to resolve it into an object
|
||||
obj = self._MODEL_CLS.objects.get(id=_id)
|
||||
self._check_for_recorded_instance(obj)
|
||||
except ObjectDoesNotExist:
|
||||
# ... If there is none, maybe we are currently processing
|
||||
# the creation of a new object (therefore no id yet), so let's continue
|
||||
obj = None
|
||||
form = self._FORM_CLS(
|
||||
request.POST or None,
|
||||
request.FILES or None,
|
||||
instance=obj,
|
||||
request=request,
|
||||
**kwargs
|
||||
)
|
||||
context = {
|
||||
"form": form,
|
||||
}
|
||||
context = BaseContext(request, context).context
|
||||
return render(request, self._TEMPLATE, context)
|
||||
|
||||
def post(self, request: HttpRequest, *args, **kwargs):
|
||||
""" POST endpoint for processing form contents of a view
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
*args ():
|
||||
**kwargs ():
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
# If there is an id provided as mapped parameter from the URL take it ...
|
||||
_id = kwargs.pop("id", None)
|
||||
try:
|
||||
# ... and try to resolve it into a record
|
||||
obj = self._MODEL_CLS.objects.get(id=_id)
|
||||
self._check_for_recorded_instance(obj)
|
||||
except ObjectDoesNotExist:
|
||||
# ... If there is none, maybe we are currently processing
|
||||
# the creation of a new object (therefore no id yet), so let's continue
|
||||
obj = None
|
||||
form = self._FORM_CLS(
|
||||
request.POST or None,
|
||||
request.FILES or None,
|
||||
instance=obj,
|
||||
request=request,
|
||||
**kwargs
|
||||
)
|
||||
# Get now the redirect url and take specifics of the obj into account for that.
|
||||
# We do not do this after saving the form to avoid side effects due to possibly changed data
|
||||
redirect_url = self._get_redirect_url(obj=obj)
|
||||
if form.is_valid():
|
||||
# Modal forms send one POST for checking on data validity. This is used to evaluate possible errors
|
||||
# on the form. The second POST (if no errors have been found) is the 'proper' one,
|
||||
# which we want to process by saving/commiting of the data to the database.
|
||||
if not is_ajax(request.META):
|
||||
# Get now the success message and take specifics of the obj into account for that
|
||||
msg_success = self._get_msg_success(obj=obj, *args, **kwargs)
|
||||
form.save()
|
||||
messages.success(
|
||||
request,
|
||||
msg_success
|
||||
)
|
||||
return HttpResponseRedirect(redirect_url)
|
||||
else:
|
||||
context = {
|
||||
"form": form,
|
||||
}
|
||||
context = BaseContext(request, context).context
|
||||
return render(request, self._TEMPLATE, context)
|
||||
|
||||
def _get_redirect_url(self, *args, **kwargs):
|
||||
""" Getter to construct a more specific, data dependant redirect URL (if needed)
|
||||
|
||||
Args:
|
||||
*args ():
|
||||
**kwargs ():
|
||||
|
||||
Returns:
|
||||
url (str): Reversed redirect url
|
||||
"""
|
||||
obj = kwargs.get("obj", None)
|
||||
if obj:
|
||||
return reverse(self._REDIRECT_URL, args=(obj.id,))
|
||||
else:
|
||||
return reverse(self._REDIRECT_URL)
|
||||
|
||||
def _get_msg_success(self, *args, **kwargs):
|
||||
""" Getter to construct a more specific, data dependant success message
|
||||
|
||||
Args:
|
||||
*args ():
|
||||
**kwargs ():
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return self._MSG_SUCCESS
|
||||
|
||||
def _check_for_recorded_instance(self, obj):
|
||||
""" Checks if the object on this view is recorded and runs some special logic if so
|
||||
|
||||
If the instance is recorded, the view should provide some information about why the user can not edit anything.
|
||||
This behaviour is only intended to mask any form for instances based on the BaseObject class.
|
||||
|
||||
There are situations where the form should be rendered regularly, despite the instance being recorded,
|
||||
e.g. for rendering deduction form contents on (recorded) eco accounts.
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
is_none = obj is None
|
||||
is_other_data_type = not isinstance(obj, BaseObject)
|
||||
|
||||
if is_none or is_other_data_type:
|
||||
# Do nothing
|
||||
return
|
||||
|
||||
if obj.is_recorded:
|
||||
# Replace default template with a blocking one
|
||||
self._TEMPLATE = "form/recorded_no_edit.html"
|
||||
@ -7,10 +7,10 @@ Created on: 19.08.22
|
||||
"""
|
||||
from konova.forms.modals import RecordModalForm
|
||||
from konova.utils.message_templates import ENTRY_RECORDED, ENTRY_UNRECORDED
|
||||
from konova.views.base import BaseModalFormView
|
||||
from konova.views.modal import AbstractModalFormView
|
||||
|
||||
|
||||
class AbstractRecordView(BaseModalFormView):
|
||||
class AbstractRecordView(AbstractModalFormView):
|
||||
_MODEL_CLS = None
|
||||
_FORM_CLS = RecordModalForm
|
||||
_MSG_SUCCESS = None
|
||||
|
||||
@ -7,11 +7,10 @@ from django.urls import reverse
|
||||
|
||||
from konova.forms.modals import RemoveModalForm
|
||||
from konova.utils.message_templates import GENERIC_REMOVED_TEMPLATE
|
||||
from konova.views.base import BaseModalFormView
|
||||
from konova.views.modal import AbstractModalFormView
|
||||
|
||||
|
||||
class BaseRemoveModalFormView(BaseModalFormView):
|
||||
_MODEL_CLS = None
|
||||
class BaseRemoveModalFormView(AbstractModalFormView):
|
||||
_FORM_CLS = RemoveModalForm
|
||||
_MSG_SUCCESS = GENERIC_REMOVED_TEMPLATE
|
||||
_REDIRECT_URL = None
|
||||
|
||||
@ -13,10 +13,10 @@ from django.utils.translation import gettext_lazy as _
|
||||
from konova.contexts import BaseContext
|
||||
from konova.forms import SimpleGeomForm
|
||||
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||
from konova.views.base import BaseView
|
||||
from konova.views.base import AbstractBaseView
|
||||
|
||||
|
||||
class BaseReportView(BaseView):
|
||||
class AbstractReportView(AbstractBaseView):
|
||||
_TEMPLATE = None
|
||||
_TAB_TITLE = _("Report {}")
|
||||
_MODEL = None
|
||||
|
||||
@ -8,10 +8,10 @@ Created on: 19.08.22
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
|
||||
from konova.utils.message_templates import NEW_RESUBMISSION_CREATED
|
||||
from konova.views.base import BaseModalFormView
|
||||
from konova.views.modal import AbstractModalFormView
|
||||
|
||||
|
||||
class AbstractResubmissionView(LoginRequiredMixin, BaseModalFormView):
|
||||
class AbstractResubmissionView(LoginRequiredMixin, AbstractModalFormView):
|
||||
_MODEL_CLS = None
|
||||
_FORM_CLS = None
|
||||
_REDIRECT_URL = None
|
||||
|
||||
@ -12,10 +12,11 @@ from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from intervention.forms.modals.share import ShareModalForm
|
||||
from konova.utils.message_templates import DATA_SHARE_SET
|
||||
from konova.views.base import BaseView, BaseModalFormView
|
||||
from konova.views.base import AbstractBaseView
|
||||
from konova.views.modal import AbstractModalFormView
|
||||
|
||||
|
||||
class AbstractShareByTokenView(LoginRequiredMixin, BaseView):
|
||||
class AbstractShareByTokenView(LoginRequiredMixin, AbstractBaseView):
|
||||
_MODEL_CLS = None
|
||||
_REDIRECT_URL = None
|
||||
|
||||
@ -69,7 +70,7 @@ class AbstractShareByTokenView(LoginRequiredMixin, BaseView):
|
||||
return True
|
||||
|
||||
|
||||
class AbstractShareFormView(LoginRequiredMixin, BaseModalFormView):
|
||||
class AbstractShareFormView(LoginRequiredMixin, AbstractModalFormView):
|
||||
_MODEL_CLS = None
|
||||
_FORM_CLS = ShareModalForm
|
||||
_MSG_SUCCESS = DATA_SHARE_SET
|
||||
|
||||
@ -12,10 +12,10 @@ from compensation.forms.modals.state import NewCompensationStateModalForm, EditC
|
||||
RemoveCompensationStateModalForm
|
||||
from konova.utils.message_templates import COMPENSATION_STATE_ADDED, COMPENSATION_STATE_EDITED, \
|
||||
COMPENSATION_STATE_REMOVED
|
||||
from konova.views.base import BaseModalFormView
|
||||
from konova.views.modal import AbstractModalFormView
|
||||
|
||||
|
||||
class AbstractCompensationStateView(LoginRequiredMixin, BaseModalFormView):
|
||||
class AbstractCompensationStateView(LoginRequiredMixin, AbstractModalFormView):
|
||||
_MODEL_CLS = None
|
||||
_FORM_CLS = None
|
||||
_REDIRECT_URL = None
|
||||
|
||||
@ -15,7 +15,7 @@ from konova.contexts import BaseContext
|
||||
from konova.decorators import default_group_required
|
||||
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||
from konova.utils.message_templates import NEW_API_TOKEN_GENERATED
|
||||
from konova.views.base import BaseModalFormView
|
||||
from konova.views.modal import AbstractModalFormView
|
||||
from user.forms.modals.api_token import NewAPITokenModalForm
|
||||
from user.models import User
|
||||
|
||||
@ -38,7 +38,7 @@ class APITokenView(View):
|
||||
context = BaseContext(request, context).context
|
||||
return render(request, template, context)
|
||||
|
||||
class NewAPITokenView(LoginRequiredMixin, BaseModalFormView):
|
||||
class NewAPITokenView(LoginRequiredMixin, AbstractModalFormView):
|
||||
_MODEL_CLS = User
|
||||
_FORM_CLS = NewAPITokenModalForm
|
||||
_MSG_SUCCESS = NEW_API_TOKEN_GENERATED
|
||||
|
||||
@ -11,14 +11,14 @@ from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from konova.contexts import BaseContext
|
||||
from konova.utils.message_templates import TEAM_LEFT, TEAM_REMOVED, TEAM_EDITED, TEAM_ADDED
|
||||
from konova.views.base import BaseModalFormView
|
||||
from konova.views.modal import AbstractModalFormView
|
||||
from user.forms.modals.team import LeaveTeamModalForm, RemoveTeamModalForm, EditTeamModalForm, NewTeamModalForm
|
||||
from user.forms.team import TeamDataForm
|
||||
from user.models import Team
|
||||
from user.views.users import UserBaseView
|
||||
|
||||
|
||||
class TeamDetailModalView(LoginRequiredMixin, BaseModalFormView):
|
||||
class TeamDetailModalView(LoginRequiredMixin, AbstractModalFormView):
|
||||
_FORM_CLS = TeamDataForm
|
||||
_MODEL_CLS = Team
|
||||
|
||||
@ -45,7 +45,7 @@ class TeamIndexView(LoginRequiredMixin, UserBaseView):
|
||||
return render(request, self._TEMPLATE, context)
|
||||
|
||||
|
||||
class BaseTeamView(LoginRequiredMixin, BaseModalFormView):
|
||||
class BaseTeamView(LoginRequiredMixin, AbstractModalFormView):
|
||||
_REDIRECT_URL = "user:team-index"
|
||||
_MODEL_CLS = Team
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
|
||||
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||
from konova.views.base import BaseView, BaseModalFormView
|
||||
from konova.views.modal import AbstractBaseView, AbstractModalFormView
|
||||
from user.forms.modals.user import UserContactForm
|
||||
from user.forms.user import UserNotificationForm
|
||||
from user.models import User
|
||||
@ -13,7 +13,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
from konova.contexts import BaseContext
|
||||
|
||||
|
||||
class UserBaseView(BaseView):
|
||||
class UserBaseView(AbstractBaseView):
|
||||
def _user_has_shared_access(self, user, **kwargs):
|
||||
return True
|
||||
|
||||
@ -68,7 +68,7 @@ class NotificationsView(LoginRequiredMixin, UserBaseView):
|
||||
return render(request, self._TEMPLATE, context)
|
||||
|
||||
|
||||
class ContactView(LoginRequiredMixin, BaseModalFormView):
|
||||
class ContactView(LoginRequiredMixin, AbstractModalFormView):
|
||||
_FORM_CLS = UserContactForm
|
||||
_MODEL_CLS = User
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user