From d85ebccec854e1853f1451b0aad2238f95768722 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Tue, 21 Oct 2025 21:05:45 +0200 Subject: [PATCH] # Compensation State view refactoring * refactors compensation state views for kom, ema, oek * updates tests * refactors before-after state toggling into initialization of NewCompensationStateModalForm --- compensation/forms/modals/state.py | 71 +++---------- .../tests/compensation/unit/test_forms.py | 46 ++++++-- compensation/views/compensation/state.py | 37 ++----- compensation/views/eco_account/state.py | 37 ++----- ema/views/state.py | 40 +++---- konova/views/state.py | 100 +++++------------- 6 files changed, 96 insertions(+), 235 deletions(-) diff --git a/compensation/forms/modals/state.py b/compensation/forms/modals/state.py index 3ff054d6..4a11a3ad 100644 --- a/compensation/forms/modals/state.py +++ b/compensation/forms/modals/state.py @@ -5,21 +5,17 @@ Contact: ksp-servicestelle@sgdnord.rlp.de Created on: 18.08.22 """ -from bootstrap_modal_forms.mixins import is_ajax from dal import autocomplete from django import forms -from django.contrib import messages -from django.http import HttpResponseRedirect, HttpRequest -from django.shortcuts import render from django.utils.translation import gettext_lazy as _ from codelist.models import KonovaCode from codelist.settings import CODELIST_BIOTOPES_ID, \ CODELIST_BIOTOPES_EXTRA_CODES_FULL_ID +from compensation.models import CompensationState from intervention.inputs import CompensationStateTreeRadioSelect -from konova.contexts import BaseContext from konova.forms.modals import RemoveModalForm, BaseModalForm -from konova.utils.message_templates import COMPENSATION_STATE_EDITED, FORM_INVALID, ADDED_COMPENSATION_STATE +from konova.utils.message_templates import COMPENSATION_STATE_EDITED, ADDED_COMPENSATION_STATE class NewCompensationStateModalForm(BaseModalForm): @@ -68,10 +64,13 @@ class NewCompensationStateModalForm(BaseModalForm): ) ) + _is_before_state: bool = False + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.form_title = _("New state") self.form_caption = _("Insert data for the new state") + self._is_before_state = bool(self.request.GET.get("before", False)) choices = KonovaCode.objects.filter( code_lists__in=[CODELIST_BIOTOPES_ID], is_archived=False, @@ -83,65 +82,19 @@ class NewCompensationStateModalForm(BaseModalForm): ] self.fields["biotope_type"].choices = choices - def save(self, is_before_state: bool = False): - state = self.instance.add_state(self, is_before_state) + def save(self): + state = self.instance.add_state(self, self._is_before_state) self.instance.mark_as_edited(self.user, self.request, ADDED_COMPENSATION_STATE) return state - def process_request(self, request: HttpRequest, msg_success: str = _("Object removed"), msg_error: str = FORM_INVALID, redirect_url: str = None): - """ Generic processing of request - - Wraps the request processing logic, so we don't need the same code everywhere a RemoveModalForm is being used - - +++ - The generic method from super class can not be used, since we need to do some request parameter check in here. - +++ - - Args: - request (HttpRequest): The incoming request - msg_success (str): The message in case of successful removing - msg_error (str): The message in case of an error - - Returns: - - """ - redirect_url = redirect_url if redirect_url is not None else request.META.get("HTTP_REFERER", "home") - template = self._TEMPLATE - if request.method == "POST": - if self.is_valid(): - # Modal forms send one POST for checking on data validity. This can be used to return possible errors - # on the form. A second POST (if no errors occured) is sent afterwards and needs to process the - # saving/commiting of the data to the database. is_ajax() performs this check. The first request is - # an ajax call, the second is a regular form POST. - if not is_ajax(request.META): - is_before_state = bool(request.GET.get("before", False)) - self.save(is_before_state=is_before_state) - messages.success( - request, - msg_success - ) - return HttpResponseRedirect(redirect_url) - else: - context = { - "form": self, - } - context = BaseContext(request, context).context - return render(request, template, context) - elif request.method == "GET": - context = { - "form": self, - } - context = BaseContext(request, context).context - return render(request, template, context) - else: - raise NotImplementedError - class EditCompensationStateModalForm(NewCompensationStateModalForm): state = None def __init__(self, *args, **kwargs): - self.state = kwargs.pop("state", None) + state_id = kwargs.pop("state_id", None) + self.state = CompensationState.objects.get(id=state_id) + super().__init__(*args, **kwargs) self.form_title = _("Edit state") biotope_type_id = self.state.biotope_type.id if self.state.biotope_type else None @@ -172,8 +125,8 @@ class RemoveCompensationStateModalForm(RemoveModalForm): state = None def __init__(self, *args, **kwargs): - state = kwargs.pop("state", None) - self.state = state + state_id = kwargs.pop("state_id", None) + self.state = CompensationState.objects.get(id=state_id) super().__init__(*args, **kwargs) def save(self): diff --git a/compensation/tests/compensation/unit/test_forms.py b/compensation/tests/compensation/unit/test_forms.py index dd804b77..0c0831b4 100644 --- a/compensation/tests/compensation/unit/test_forms.py +++ b/compensation/tests/compensation/unit/test_forms.py @@ -190,12 +190,20 @@ class NewCompensationStateModalFormTestCase(BaseTestCase): self.assertEqual(self.compensation.before_states.count(), 0) self.assertEqual(self.compensation.after_states.count(), 0) - form = NewCompensationStateModalForm(data, request=self.request, instance=self.compensation) - + self.request.GET._mutable = True + self.request.GET.update( + { + "before": True, + } + ) + self.request.GET._mutable = False + form = NewCompensationStateModalForm( + data, + request=self.request, + instance=self.compensation, + ) self.assertTrue(form.is_valid(), msg=form.errors) - - is_before_state = True - state = form.save(is_before_state) + state = form.save() self.assertEqual(self.compensation.before_states.count(), 1) self.assertEqual(self.compensation.after_states.count(), 0) @@ -209,8 +217,16 @@ class NewCompensationStateModalFormTestCase(BaseTestCase): self.assertEqual(last_log.action, UserAction.EDITED) self.assertEqual(last_log.comment, ADDED_COMPENSATION_STATE) - is_before_state = False - state = form.save(is_before_state) + self.request.GET._mutable = True + del self.request.GET["before"] + self.request.GET._mutable = False + form = NewCompensationStateModalForm( + data, + request=self.request, + instance=self.compensation, + ) + self.assertTrue(form.is_valid(), msg=form.errors) + state = form.save() self.assertEqual(self.compensation.before_states.count(), 1) self.assertEqual(self.compensation.after_states.count(), 1) @@ -234,7 +250,11 @@ class EditCompensationStateModalFormTestCase(NewCompensationStateModalFormTestCa self.compensation.after_states.add(self.comp_state) def test_init(self): - form = EditCompensationStateModalForm(request=self.request, instance=self.compensation, state=self.comp_state) + form = EditCompensationStateModalForm( + request=self.request, + instance=self.compensation, + state_id=self.comp_state.id + ) self.assertEqual(form.state, self.comp_state) self.assertEqual(form.form_title, str(_("Edit state"))) @@ -265,7 +285,7 @@ class EditCompensationStateModalFormTestCase(NewCompensationStateModalFormTestCa data, request=self.request, instance=self.compensation, - state=self.comp_state + state_id=self.comp_state.id ) self.assertTrue(form.is_valid(), msg=form.errors) @@ -286,7 +306,11 @@ class RemoveCompensationStateModalFormTestCase(EditCompensationStateModalFormTes super().setUp() def test_init(self): - form = RemoveCompensationStateModalForm(request=self.request, instance=self.compensation, state=self.comp_state) + form = RemoveCompensationStateModalForm( + request=self.request, + instance=self.compensation, + state_id=self.comp_state.id + ) self.assertEqual(form.state, self.comp_state) @@ -298,7 +322,7 @@ class RemoveCompensationStateModalFormTestCase(EditCompensationStateModalFormTes data, request=self.request, instance=self.compensation, - state=self.comp_state + state_id=self.comp_state.id ) self.assertTrue(form.is_valid(), msg=form.errors) diff --git a/compensation/views/compensation/state.py b/compensation/views/compensation/state.py index 8fffbbd7..590ddc52 100644 --- a/compensation/views/compensation/state.py +++ b/compensation/views/compensation/state.py @@ -5,46 +5,21 @@ Contact: ksp-servicestelle@sgdnord.rlp.de Created on: 19.08.22 """ -from django.contrib.auth.decorators import login_required -from django.utils.decorators import method_decorator - from compensation.models import Compensation -from konova.decorators import shared_access_required, default_group_required, login_required_modal from konova.views.state import AbstractNewCompensationStateView, AbstractEditCompensationStateView, \ AbstractRemoveCompensationStateView class NewCompensationStateView(AbstractNewCompensationStateView): - model = Compensation - redirect_url = "compensation:detail" - - @method_decorator(login_required_modal) - @method_decorator(login_required) - @method_decorator(default_group_required) - @method_decorator(shared_access_required(Compensation, "id")) - def dispatch(self, request, *args, **kwargs): - return super().dispatch(request, *args, **kwargs) + _MODEL_CLS = Compensation + _REDIRECT_URL = "compensation:detail" class EditCompensationStateView(AbstractEditCompensationStateView): - model = Compensation - redirect_url = "compensation:detail" - - @method_decorator(login_required_modal) - @method_decorator(login_required) - @method_decorator(default_group_required) - @method_decorator(shared_access_required(Compensation, "id")) - def dispatch(self, request, *args, **kwargs): - return super().dispatch(request, *args, **kwargs) + _MODEL_CLS = Compensation + _REDIRECT_URL = "compensation:detail" class RemoveCompensationStateView(AbstractRemoveCompensationStateView): - model = Compensation - redirect_url = "compensation:detail" - - @method_decorator(login_required_modal) - @method_decorator(login_required) - @method_decorator(default_group_required) - @method_decorator(shared_access_required(Compensation, "id")) - def dispatch(self, request, *args, **kwargs): - return super().dispatch(request, *args, **kwargs) + _MODEL_CLS = Compensation + _REDIRECT_URL = "compensation:detail" diff --git a/compensation/views/eco_account/state.py b/compensation/views/eco_account/state.py index 1a28491a..6765810e 100644 --- a/compensation/views/eco_account/state.py +++ b/compensation/views/eco_account/state.py @@ -5,46 +5,21 @@ Contact: ksp-servicestelle@sgdnord.rlp.de Created on: 19.08.22 """ -from django.contrib.auth.decorators import login_required -from django.utils.decorators import method_decorator - from compensation.models import EcoAccount -from konova.decorators import shared_access_required, default_group_required, login_required_modal from konova.views.state import AbstractNewCompensationStateView, AbstractEditCompensationStateView, \ AbstractRemoveCompensationStateView class NewEcoAccountStateView(AbstractNewCompensationStateView): - model = EcoAccount - redirect_url = "compensation:acc:detail" - - @method_decorator(login_required_modal) - @method_decorator(login_required) - @method_decorator(default_group_required) - @method_decorator(shared_access_required(EcoAccount, "id")) - def dispatch(self, request, *args, **kwargs): - return super().dispatch(request, *args, **kwargs) + _MODEL_CLS = EcoAccount + _REDIRECT_URL = "compensation:acc:detail" class EditEcoAccountStateView(AbstractEditCompensationStateView): - model = EcoAccount - redirect_url = "compensation:acc:detail" - - @method_decorator(login_required_modal) - @method_decorator(login_required) - @method_decorator(default_group_required) - @method_decorator(shared_access_required(EcoAccount, "id")) - def dispatch(self, request, *args, **kwargs): - return super().dispatch(request, *args, **kwargs) + _MODEL_CLS = EcoAccount + _REDIRECT_URL = "compensation:acc:detail" class RemoveEcoAccountStateView(AbstractRemoveCompensationStateView): - model = EcoAccount - redirect_url = "compensation:acc:detail" - - @method_decorator(login_required_modal) - @method_decorator(login_required) - @method_decorator(default_group_required) - @method_decorator(shared_access_required(EcoAccount, "id")) - def dispatch(self, request, *args, **kwargs): - return super().dispatch(request, *args, **kwargs) + _MODEL_CLS = EcoAccount + _REDIRECT_URL = "compensation:acc:detail" diff --git a/ema/views/state.py b/ema/views/state.py index e8e489dc..4c3009ed 100644 --- a/ema/views/state.py +++ b/ema/views/state.py @@ -5,46 +5,30 @@ Contact: ksp-servicestelle@sgdnord.rlp.de Created on: 19.08.22 """ -from django.contrib.auth.decorators import login_required -from django.utils.decorators import method_decorator - from ema.models import Ema -from konova.decorators import conservation_office_group_required, shared_access_required, login_required_modal from konova.views.state import AbstractNewCompensationStateView, AbstractEditCompensationStateView, \ AbstractRemoveCompensationStateView class NewEmaStateView(AbstractNewCompensationStateView): - model = Ema - redirect_url = "ema:detail" + _MODEL_CLS = Ema + _REDIRECT_URL = "ema:detail" - @method_decorator(login_required_modal) - @method_decorator(login_required) - @method_decorator(conservation_office_group_required) - @method_decorator(shared_access_required(Ema, "id")) - def dispatch(self, request, *args, **kwargs): - return super().dispatch(request, *args, **kwargs) + def _user_has_permission(self, user): + return user.is_ets_user() class EditEmaStateView(AbstractEditCompensationStateView): - model = Ema - redirect_url = "ema:detail" + _MODEL_CLS = Ema + _REDIRECT_URL = "ema:detail" - @method_decorator(login_required_modal) - @method_decorator(login_required) - @method_decorator(conservation_office_group_required) - @method_decorator(shared_access_required(Ema, "id")) - def dispatch(self, request, *args, **kwargs): - return super().dispatch(request, *args, **kwargs) + def _user_has_permission(self, user): + return user.is_ets_user() class RemoveEmaStateView(AbstractRemoveCompensationStateView): - model = Ema - redirect_url = "ema:detail" + _MODEL_CLS = Ema + _REDIRECT_URL = "ema:detail" - @method_decorator(login_required_modal) - @method_decorator(login_required) - @method_decorator(conservation_office_group_required) - @method_decorator(shared_access_required(Ema, "id")) - def dispatch(self, request, *args, **kwargs): - return super().dispatch(request, *args, **kwargs) + def _user_has_permission(self, user): + return user.is_ets_user() diff --git a/konova/views/state.py b/konova/views/state.py index 1165e08b..419c573b 100644 --- a/konova/views/state.py +++ b/konova/views/state.py @@ -5,103 +5,53 @@ Contact: ksp-servicestelle@sgdnord.rlp.de Created on: 22.08.22 """ -from django.shortcuts import get_object_or_404 +from django.contrib.auth.mixins import LoginRequiredMixin from django.urls import reverse -from django.views import View from compensation.forms.modals.state import NewCompensationStateModalForm, EditCompensationStateModalForm, \ RemoveCompensationStateModalForm -from compensation.models import CompensationState from konova.utils.message_templates import COMPENSATION_STATE_ADDED, COMPENSATION_STATE_EDITED, \ COMPENSATION_STATE_REMOVED +from konova.views.base import BaseModalFormView -class AbstractCompensationStateView(View): - model = None - redirect_url = None +class AbstractCompensationStateView(LoginRequiredMixin, BaseModalFormView): + _MODEL_CLS = None + _FORM_CLS = None + _REDIRECT_URL = None class Meta: abstract = True + def _user_has_permission(self, user): + return user.is_default_user() + + def _get_redirect_url(self, *args, **kwargs): + obj = kwargs.get("obj", None) + assert obj is not None + return reverse(self._REDIRECT_URL, args=(obj.id,)) + "#related_data" class AbstractNewCompensationStateView(AbstractCompensationStateView): + _MODEL_CLS = None + _FORM_CLS = NewCompensationStateModalForm + _MSG_SUCCESS = COMPENSATION_STATE_ADDED + class Meta: abstract = True - def get(self, request, id: str): - """ Renders a form for adding new states - - Args: - request (HttpRequest): The incoming request - id (str): The object's id to which the new state will be related - - Returns: - - """ - obj = get_object_or_404(self.model, id=id) - form = NewCompensationStateModalForm(request.POST or None, instance=obj, request=request) - return form.process_request( - request, - msg_success=COMPENSATION_STATE_ADDED, - redirect_url=reverse(self.redirect_url, args=(id,)) + "#related_data" - ) - - def post(self, request, id: str): - return self.get(request, id) - - class AbstractEditCompensationStateView(AbstractCompensationStateView): + _MODEL_CLS = None + _FORM_CLS = EditCompensationStateModalForm + _MSG_SUCCESS = COMPENSATION_STATE_EDITED + class Meta: abstract = True - def get(self, request, id: str, state_id: str): - """ Renders a form for editing a state - - Args: - request (HttpRequest): The incoming request - id (str): The object id - state_id (str): The state's id - - Returns: - - """ - obj = get_object_or_404(self.model, id=id) - state = get_object_or_404(CompensationState, id=state_id) - form = EditCompensationStateModalForm(request.POST or None, instance=obj, state=state, request=request) - return form.process_request( - request, - msg_success=COMPENSATION_STATE_EDITED, - redirect_url=reverse(self.redirect_url, args=(id,)) + "#related_data" - ) - - def post(self, request, id: str, state_id: str): - return self.get(request, id, state_id) - class AbstractRemoveCompensationStateView(AbstractCompensationStateView): + _MODEL_CLS = None + _FORM_CLS = RemoveCompensationStateModalForm + _MSG_SUCCESS = COMPENSATION_STATE_REMOVED + class Meta: abstract = True - - def get(self, request, id: str, state_id: str): - """ Renders a form for removing astate - - Args: - request (HttpRequest): The incoming request - id (str): The object id - state_id (str): The state's id - - Returns: - - """ - obj = get_object_or_404(self.model, id=id) - state = get_object_or_404(CompensationState, id=state_id) - form = RemoveCompensationStateModalForm(request.POST or None, instance=obj, state=state, request=request) - return form.process_request( - request, - msg_success=COMPENSATION_STATE_REMOVED, - redirect_url=reverse(self.redirect_url, args=(id,)) + "#related_data" - ) - - def post(self, request, id: str, state_id: str): - return self.get(request, id, state_id) -