diff --git a/compensation/urls/compensation.py b/compensation/urls/compensation.py index 6602005..75ec929 100644 --- a/compensation/urls/compensation.py +++ b/compensation/urls/compensation.py @@ -6,7 +6,17 @@ Created on: 24.08.21 """ from django.urls import path -from compensation.views.compensation import * + +from compensation.views.compensation.document import edit_document_view, new_document_view, remove_document_view, \ + get_document_view +from compensation.views.compensation.resubmission import create_resubmission_view +from compensation.views.compensation.report import report_view +from compensation.views.compensation.deadline import deadline_new_view, deadline_edit_view, deadline_remove_view +from compensation.views.compensation.action import action_edit_view, action_new_view, action_remove_view +from compensation.views.compensation.state import state_new_view, state_remove_view, state_edit_view +from compensation.views.compensation.compensation import index_view, new_view, new_id_view, detail_view, edit_view, \ + remove_view +from compensation.views.compensation.log import log_view urlpatterns = [ # Main compensation diff --git a/compensation/views/__init__.py b/compensation/views/__init__.py index db03b5a..dc6f522 100644 --- a/compensation/views/__init__.py +++ b/compensation/views/__init__.py @@ -5,6 +5,5 @@ Contact: michel.peltriaux@sgdnord.rlp.de Created on: 16.11.21 """ -from .compensation import * from .eco_account import * from .payment import * diff --git a/compensation/views/compensation.py b/compensation/views/compensation.py deleted file mode 100644 index c1bec04..0000000 --- a/compensation/views/compensation.py +++ /dev/null @@ -1,685 +0,0 @@ -from django.contrib.auth.decorators import login_required -from django.core.exceptions import ObjectDoesNotExist -from django.db.models import Sum -from django.http import HttpRequest, JsonResponse -from django.shortcuts import render -from django.utils.translation import gettext_lazy as _ - -from compensation.forms.compensation import NewCompensationForm, EditCompensationForm -from compensation.forms.modals.state import NewCompensationStateModalForm, RemoveCompensationStateModalForm, EditCompensationStateModalForm -from compensation.forms.modals.document import NewCompensationDocumentModalForm -from compensation.forms.modals.compensation_action import NewCompensationActionModalForm, \ - EditCompensationActionModalForm, RemoveCompensationActionModalForm -from compensation.forms.modals.deadline import NewDeadlineModalForm, EditDeadlineModalForm -from compensation.models import Compensation, CompensationState, CompensationAction, CompensationDocument -from compensation.tables.compensation import CompensationTable -from intervention.models import Intervention -from konova.contexts import BaseContext -from konova.decorators import * -from konova.forms.modals import RemoveModalForm,RemoveDeadlineModalForm, EditDocumentModalForm, \ - ResubmissionModalForm -from konova.forms import SimpleGeomForm -from konova.models import Deadline -from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER -from konova.utils.documents import get_document, remove_document -from konova.utils.generators import generate_qr_code -from konova.utils.message_templates import FORM_INVALID, IDENTIFIER_REPLACED, \ - CHECKED_RECORDED_RESET, COMPENSATION_ADDED_TEMPLATE, COMPENSATION_REMOVED_TEMPLATE, DOCUMENT_ADDED, \ - COMPENSATION_STATE_REMOVED, COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_ADDED, \ - DEADLINE_ADDED, DEADLINE_REMOVED, DOCUMENT_EDITED, COMPENSATION_STATE_EDITED, COMPENSATION_ACTION_EDITED, \ - DEADLINE_EDITED, RECORDED_BLOCKS_EDIT, PARAMS_INVALID, DATA_CHECKED_PREVIOUSLY_TEMPLATE -from konova.utils.user_checks import in_group - - -@login_required -@any_group_check -def index_view(request: HttpRequest): - """ - Renders the index view for compensation - - Args: - request (HttpRequest): The incoming request - - Returns: - A rendered view - """ - template = "generic_index.html" - compensations = Compensation.objects.filter( - deleted=None, # only show those which are not deleted individually - intervention__deleted=None, # and don't show the ones whose intervention has been deleted - ) - table = CompensationTable( - request=request, - queryset=compensations - ) - context = { - "table": table, - TAB_TITLE_IDENTIFIER: _("Compensations - Overview"), - } - context = BaseContext(request, context).context - return render(request, template, context) - - -@login_required -@default_group_required -@shared_access_required(Intervention, "intervention_id") -def new_view(request: HttpRequest, intervention_id: str = None): - """ - Renders a view for a new compensation creation - - Args: - request (HttpRequest): The incoming request - - Returns: - - """ - template = "compensation/form/view.html" - if intervention_id is not None: - try: - intervention = Intervention.objects.get(id=intervention_id) - except ObjectDoesNotExist: - messages.error(request, PARAMS_INVALID) - return redirect("home") - if intervention.is_recorded: - messages.info( - request, - RECORDED_BLOCKS_EDIT - ) - return redirect("intervention:detail", id=intervention_id) - - data_form = NewCompensationForm(request.POST or None, intervention_id=intervention_id) - geom_form = SimpleGeomForm(request.POST or None, read_only=False) - if request.method == "POST": - if data_form.is_valid() and geom_form.is_valid(): - generated_identifier = data_form.cleaned_data.get("identifier", None) - comp = data_form.save(request.user, geom_form) - if generated_identifier != comp.identifier: - messages.info( - request, - IDENTIFIER_REPLACED.format( - generated_identifier, - comp.identifier - ) - ) - messages.success(request, COMPENSATION_ADDED_TEMPLATE.format(comp.identifier)) - return redirect("compensation:detail", id=comp.id) - else: - messages.error(request, FORM_INVALID, extra_tags="danger",) - else: - # For clarification: nothing in this case - pass - context = { - "form": data_form, - "geom_form": geom_form, - TAB_TITLE_IDENTIFIER: _("New compensation"), - } - context = BaseContext(request, context).context - return render(request, template, context) - - -@login_required -@default_group_required -def new_id_view(request: HttpRequest): - """ JSON endpoint - - Provides fetching of free identifiers for e.g. AJAX calls - - """ - tmp = Compensation() - identifier = tmp.generate_new_identifier() - while Compensation.objects.filter(identifier=identifier).exists(): - identifier = tmp.generate_new_identifier() - return JsonResponse( - data={ - "gen_data": identifier - } - ) - - -@login_required -@default_group_required -@shared_access_required(Compensation, "id") -def edit_view(request: HttpRequest, id: str): - """ - Renders a view for editing compensations - - Args: - request (HttpRequest): The incoming request - - Returns: - - """ - template = "compensation/form/view.html" - # Get object from db - comp = get_object_or_404(Compensation, id=id) - if comp.is_recorded: - messages.info( - request, - RECORDED_BLOCKS_EDIT - ) - return redirect("compensation:detail", id=id) - - # Create forms, initialize with values from db/from POST request - data_form = EditCompensationForm(request.POST or None, instance=comp) - geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=comp) - if request.method == "POST": - if data_form.is_valid() and geom_form.is_valid(): - # Preserve state of intervention recorded/checked to determine whether the user must be informed or not - # about a change of the recorded/checked state - intervention_recorded = comp.intervention.recorded is not None - intervention_checked = comp.intervention.checked is not None - - # The data form takes the geom form for processing, as well as the performing user - comp = data_form.save(request.user, geom_form) - if intervention_recorded or intervention_checked: - messages.info(request, CHECKED_RECORDED_RESET) - messages.success(request, _("Compensation {} edited").format(comp.identifier)) - return redirect("compensation:detail", id=comp.id) - else: - messages.error(request, FORM_INVALID, extra_tags="danger",) - else: - # For clarification: nothing in this case - pass - context = { - "form": data_form, - "geom_form": geom_form, - TAB_TITLE_IDENTIFIER: _("Edit {}").format(comp.identifier), - } - context = BaseContext(request, context).context - return render(request, template, context) - - -@login_required -@any_group_check -def detail_view(request: HttpRequest, id: str): - """ Renders a detail view for a compensation - - Args: - request (HttpRequest): The incoming request - id (str): The compensation's id - - Returns: - - """ - template = "compensation/detail/compensation/view.html" - comp = get_object_or_404(Compensation, id=id) - geom_form = SimpleGeomForm(instance=comp) - parcels = comp.get_underlying_parcels() - _user = request.user - is_data_shared = comp.intervention.is_shared_with(_user) - - # Order states according to surface - before_states = comp.before_states.all().prefetch_related("biotope_type").order_by("-surface") - after_states = comp.after_states.all().prefetch_related("biotope_type").order_by("-surface") - actions = comp.actions.all().prefetch_related("action_type") - - # Precalculate logical errors between before- and after-states - # Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling - sum_before_states = before_states.aggregate(Sum("surface"))["surface__sum"] or 0 - sum_after_states = after_states.aggregate(Sum("surface"))["surface__sum"] or 0 - diff_states = abs(sum_before_states - sum_after_states) - - request = comp.set_status_messages(request) - - last_checked = comp.intervention.get_last_checked_action() - last_checked_tooltip = "" - if last_checked: - last_checked_tooltip = DATA_CHECKED_PREVIOUSLY_TEMPLATE.format(last_checked.get_timestamp_str_formatted(), last_checked.user) - - context = { - "obj": comp, - "last_checked": last_checked, - "last_checked_tooltip": last_checked_tooltip, - "geom_form": geom_form, - "parcels": parcels, - "has_access": is_data_shared, - "actions": actions, - "before_states": before_states, - "after_states": after_states, - "sum_before_states": sum_before_states, - "sum_after_states": sum_after_states, - "diff_states": diff_states, - "is_default_member": in_group(_user, DEFAULT_GROUP), - "is_zb_member": in_group(_user, ZB_GROUP), - "is_ets_member": in_group(_user, ETS_GROUP), - "LANIS_LINK": comp.get_LANIS_link(), - TAB_TITLE_IDENTIFIER: f"{comp.identifier} - {comp.title}", - "has_finished_deadlines": comp.get_finished_deadlines().exists(), - } - context = BaseContext(request, context).context - return render(request, template, context) - - -@login_required -@default_group_required -@shared_access_required(Compensation, "id") -def log_view(request: HttpRequest, id: str): - """ Renders a log view using modal - - Args: - request (HttpRequest): The incoming request - id (str): The compensation's id - - Returns: - - """ - comp = get_object_or_404(Compensation, id=id) - template = "modal/modal_generic.html" - body_template = "log.html" - - context = { - "modal_body_template": body_template, - "log": comp.log.all(), - "modal_title": _("Log"), - } - context = BaseContext(request, context).context - return render(request, template, context) - - -@login_required -@default_group_required -@shared_access_required(Compensation, "id") -def remove_view(request: HttpRequest, id: str): - """ Renders a modal view for removing the compensation - - Args: - request (HttpRequest): The incoming request - id (str): The compensation's id - - Returns: - - """ - comp = get_object_or_404(Compensation, id=id) - form = RemoveModalForm(request.POST or None, instance=comp, request=request) - return form.process_request( - request=request, - msg_success=COMPENSATION_REMOVED_TEMPLATE.format(comp.identifier), - redirect_url=reverse("compensation:index"), - ) - - -@login_required -@default_group_required -@shared_access_required(Compensation, "id") -def new_document_view(request: HttpRequest, id: str): - """ Renders a form for uploading new documents - - Args: - request (HttpRequest): The incoming request - id (str): The compensation's id to which the new document will be related - Returns: - - """ - comp = get_object_or_404(Compensation, id=id) - form = NewCompensationDocumentModalForm(request.POST or None, request.FILES or None, instance=comp, request=request) - return form.process_request( - request, - msg_success=DOCUMENT_ADDED, - redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data" - ) - - -@login_required -@default_group_required -@shared_access_required(Compensation, "id") -def get_document_view(request: HttpRequest, id: str, doc_id: str): - """ Returns the document as downloadable file - - Wraps the generic document fetcher function from konova.utils. - - Args: - request (HttpRequest): The incoming request - id (str): The compensation id - doc_id (str): The document id - - Returns: - - """ - comp = get_object_or_404(Compensation, id=id) - doc = get_object_or_404(CompensationDocument, id=doc_id) - return get_document(doc) - - -@login_required -@default_group_required -@shared_access_required(Compensation, "id") -def remove_document_view(request: HttpRequest, id: str, doc_id: str): - """ Removes the document from the database and file system - - Wraps the generic functionality from konova.utils. - - Args: - request (HttpRequest): The incoming request - id (str): The compensation id - doc_id (str): The document id - - Returns: - - """ - comp = get_object_or_404(Compensation, id=id) - doc = get_object_or_404(CompensationDocument, id=doc_id) - return remove_document( - request, - doc - ) - - -@login_required -@default_group_required -@shared_access_required(Compensation, "id") -def edit_document_view(request: HttpRequest, id: str, doc_id: str): - """ Removes the document from the database and file system - - Wraps the generic functionality from konova.utils. - - Args: - request (HttpRequest): The incoming request - id (str): The compensation id - doc_id (str): The document id - - Returns: - - """ - comp = get_object_or_404(Compensation, id=id) - doc = get_object_or_404(CompensationDocument, id=doc_id) - form = EditDocumentModalForm(request.POST or None, request.FILES or None, instance=comp, document=doc, request=request) - return form.process_request( - request, - DOCUMENT_EDITED, - reverse("compensation:detail", args=(id,)) + "#related_data" - ) - - -@login_required -@default_group_required -@shared_access_required(Compensation, "id") -def state_new_view(request: HttpRequest, id: str): - """ Renders a form for adding new states for a compensation - - Args: - request (HttpRequest): The incoming request - id (str): The compensation's id to which the new state will be related - - Returns: - - """ - comp = get_object_or_404(Compensation, id=id) - form = NewCompensationStateModalForm(request.POST or None, instance=comp, request=request) - return form.process_request( - request, - msg_success=COMPENSATION_STATE_ADDED, - redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data" - ) - - -@login_required -@default_group_required -@shared_access_required(Compensation, "id") -def action_new_view(request: HttpRequest, id: str): - """ Renders a form for adding new actions for a compensation - - Args: - request (HttpRequest): The incoming request - id (str): The compensation's id to which the new state will be related - - Returns: - - """ - comp = get_object_or_404(Compensation, id=id) - form = NewCompensationActionModalForm(request.POST or None, instance=comp, request=request) - return form.process_request( - request, - msg_success=COMPENSATION_ACTION_ADDED, - redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data" - ) - - -@login_required -@default_group_required -@shared_access_required(Compensation, "id") -def action_edit_view(request: HttpRequest, id: str, action_id: str): - """ Renders a form for editing actions for a compensation - - Args: - request (HttpRequest): The incoming request - id (str): The compensation's id - action_id (str): The action's id - - Returns: - - """ - comp = get_object_or_404(Compensation, id=id) - action = get_object_or_404(CompensationAction, id=action_id) - form = EditCompensationActionModalForm(request.POST or None, instance=comp, action=action, request=request) - return form.process_request( - request, - msg_success=COMPENSATION_ACTION_EDITED, - redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data" - ) - - -@login_required -@default_group_required -@shared_access_required(Compensation, "id") -def deadline_new_view(request: HttpRequest, id: str): - """ Renders a form for adding new states for a compensation - - Args: - request (HttpRequest): The incoming request - id (str): The compensation's id to which the new state will be related - - Returns: - - """ - comp = get_object_or_404(Compensation, id=id) - form = NewDeadlineModalForm(request.POST or None, instance=comp, request=request) - return form.process_request( - request, - msg_success=DEADLINE_ADDED, - redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data" - ) - - -@login_required -@default_group_required -@shared_access_required(Compensation, "id") -def deadline_edit_view(request: HttpRequest, id: str, deadline_id: str): - """ Renders a form for editing deadlines from a compensation - - Args: - request (HttpRequest): The incoming request - id (str): The compensation's id - deadline_id (str): The deadline's id - - Returns: - - """ - comp = get_object_or_404(Compensation, id=id) - deadline = get_object_or_404(Deadline, id=deadline_id) - form = EditDeadlineModalForm(request.POST or None, instance=comp, deadline=deadline, request=request) - return form.process_request( - request, - msg_success=DEADLINE_EDITED, - redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data" - ) - - -@login_required -@default_group_required -@shared_access_required(Compensation, "id") -def deadline_remove_view(request: HttpRequest, id: str, deadline_id: str): - """ Renders a form for removing deadlines from a compensation - - Args: - request (HttpRequest): The incoming request - id (str): The compensation's id - deadline_id (str): The deadline's id - - Returns: - - """ - comp = get_object_or_404(Compensation, id=id) - deadline = get_object_or_404(Deadline, id=deadline_id) - form = RemoveDeadlineModalForm(request.POST or None, instance=comp, deadline=deadline, request=request) - return form.process_request( - request, - msg_success=DEADLINE_REMOVED, - redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data" - ) - - -@login_required -@default_group_required -@shared_access_required(Compensation, "id") -def state_remove_view(request: HttpRequest, id: str, state_id: str): - """ Renders a form for removing a compensation state - - Args: - request (HttpRequest): The incoming request - id (str): The compensation's id - state_id (str): The state's id - - Returns: - - """ - comp = get_object_or_404(Compensation, id=id) - state = get_object_or_404(CompensationState, id=state_id) - form = RemoveCompensationStateModalForm(request.POST or None, instance=comp, state=state, request=request) - return form.process_request( - request, - msg_success=COMPENSATION_STATE_REMOVED, - redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data" - ) - - -@login_required -@default_group_required -@shared_access_required(Compensation, "id") -def state_edit_view(request: HttpRequest, id: str, state_id: str): - """ Renders a form for editing a compensation state - - Args: - request (HttpRequest): The incoming request - id (str): The compensation's id - state_id (str): The state's id - - Returns: - - """ - comp = get_object_or_404(Compensation, id=id) - state = get_object_or_404(CompensationState, id=state_id) - form = EditCompensationStateModalForm(request.POST or None, instance=comp, state=state, request=request) - return form.process_request( - request, - msg_success=COMPENSATION_STATE_EDITED, - redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data" - ) - - -@login_required -@default_group_required -@shared_access_required(Compensation, "id") -def action_remove_view(request: HttpRequest, id: str, action_id: str): - """ Renders a form for removing a compensation action - - Args: - request (HttpRequest): The incoming request - id (str): The compensation's id - id (str): The action's id - - Returns: - - """ - comp = get_object_or_404(Compensation, id=id) - action = get_object_or_404(CompensationAction, id=action_id) - form = RemoveCompensationActionModalForm(request.POST or None, instance=comp, action=action, request=request) - return form.process_request( - request, - msg_success=COMPENSATION_ACTION_REMOVED, - redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data" - ) - - -def report_view(request: HttpRequest, id: str): - """ Renders the public report view - - Args: - request (HttpRequest): The incoming request - id (str): The id of the intervention - - Returns: - - """ - # Reuse the compensation report template since compensations are structurally identical - template = "compensation/report/compensation/report.html" - comp = get_object_or_404(Compensation, id=id) - - tab_title = _("Report {}").format(comp.identifier) - # If intervention is not recorded (yet or currently) we need to render another template without any data - if not comp.is_ready_for_publish(): - template = "report/unavailable.html" - context = { - TAB_TITLE_IDENTIFIER: tab_title, - } - context = BaseContext(request, context).context - return render(request, template, context) - - # Prepare data for map viewer - geom_form = SimpleGeomForm( - instance=comp - ) - parcels = comp.get_underlying_parcels() - - qrcode_url = request.build_absolute_uri(reverse("compensation:report", args=(id,))) - qrcode_img = generate_qr_code(qrcode_url, 10) - qrcode_lanis_url = comp.get_LANIS_link() - qrcode_img_lanis = generate_qr_code(qrcode_lanis_url, 7) - - # Order states by surface - before_states = comp.before_states.all().order_by("-surface").prefetch_related("biotope_type") - after_states = comp.after_states.all().order_by("-surface").prefetch_related("biotope_type") - actions = comp.actions.all().prefetch_related("action_type") - - context = { - "obj": comp, - "qrcode": { - "img": qrcode_img, - "url": qrcode_url, - }, - "qrcode_lanis": { - "img": qrcode_img_lanis, - "url": qrcode_lanis_url, - }, - "has_access": False, # disables action buttons during rendering - "before_states": before_states, - "after_states": after_states, - "geom_form": geom_form, - "parcels": parcels, - "actions": actions, - TAB_TITLE_IDENTIFIER: tab_title, - } - context = BaseContext(request, context).context - return render(request, template, context) - - -@login_required -@default_group_required -@shared_access_required(Compensation, "id") -def create_resubmission_view(request: HttpRequest, id: str): - """ Renders resubmission form for a compensation - - Args: - request (HttpRequest): The incoming request - id (str): Compensation's id - - Returns: - - """ - com = get_object_or_404(Compensation, id=id) - form = ResubmissionModalForm(request.POST or None, instance=com, request=request) - form.action_url = reverse("compensation:resubmission-create", args=(id,)) - return form.process_request( - request, - msg_success=_("Resubmission set"), - redirect_url=reverse("compensation:detail", args=(id,)) - ) diff --git a/compensation/views/compensation/__init__.py b/compensation/views/compensation/__init__.py new file mode 100644 index 0000000..3a996e1 --- /dev/null +++ b/compensation/views/compensation/__init__.py @@ -0,0 +1,7 @@ +""" +Author: Michel Peltriaux +Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany +Contact: ksp-servicestelle@sgdnord.rlp.de +Created on: 19.08.22 + +""" diff --git a/compensation/views/compensation/action.py b/compensation/views/compensation/action.py new file mode 100644 index 0000000..54f6bc3 --- /dev/null +++ b/compensation/views/compensation/action.py @@ -0,0 +1,89 @@ +""" +Author: Michel Peltriaux +Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany +Contact: ksp-servicestelle@sgdnord.rlp.de +Created on: 19.08.22 + +""" +from django.contrib.auth.decorators import login_required +from django.http import HttpRequest +from django.shortcuts import get_object_or_404 +from django.urls import reverse + +from compensation.forms.modals.compensation_action import RemoveCompensationActionModalForm, \ + EditCompensationActionModalForm, NewCompensationActionModalForm +from compensation.models import Compensation, CompensationAction +from konova.decorators import shared_access_required, default_group_required +from konova.utils.message_templates import COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_EDITED, \ + COMPENSATION_ACTION_ADDED + + +@login_required +@default_group_required +@shared_access_required(Compensation, "id") +def action_new_view(request: HttpRequest, id: str): + """ Renders a form for adding new actions for a compensation + + Args: + request (HttpRequest): The incoming request + id (str): The compensation's id to which the new state will be related + + Returns: + + """ + comp = get_object_or_404(Compensation, id=id) + form = NewCompensationActionModalForm(request.POST or None, instance=comp, request=request) + return form.process_request( + request, + msg_success=COMPENSATION_ACTION_ADDED, + redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data" + ) + + +@login_required +@default_group_required +@shared_access_required(Compensation, "id") +def action_edit_view(request: HttpRequest, id: str, action_id: str): + """ Renders a form for editing actions for a compensation + + Args: + request (HttpRequest): The incoming request + id (str): The compensation's id + action_id (str): The action's id + + Returns: + + """ + comp = get_object_or_404(Compensation, id=id) + action = get_object_or_404(CompensationAction, id=action_id) + form = EditCompensationActionModalForm(request.POST or None, instance=comp, action=action, request=request) + return form.process_request( + request, + msg_success=COMPENSATION_ACTION_EDITED, + redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data" + ) + + +@login_required +@default_group_required +@shared_access_required(Compensation, "id") +def action_remove_view(request: HttpRequest, id: str, action_id: str): + """ Renders a form for removing a compensation action + + Args: + request (HttpRequest): The incoming request + id (str): The compensation's id + id (str): The action's id + + Returns: + + """ + comp = get_object_or_404(Compensation, id=id) + action = get_object_or_404(CompensationAction, id=action_id) + form = RemoveCompensationActionModalForm(request.POST or None, instance=comp, action=action, request=request) + return form.process_request( + request, + msg_success=COMPENSATION_ACTION_REMOVED, + redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data" + ) + diff --git a/compensation/views/compensation/compensation.py b/compensation/views/compensation/compensation.py new file mode 100644 index 0000000..dd46110 --- /dev/null +++ b/compensation/views/compensation/compensation.py @@ -0,0 +1,272 @@ +""" +Author: Michel Peltriaux +Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany +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.core.exceptions import ObjectDoesNotExist +from django.db.models import Sum +from django.http import HttpRequest, JsonResponse +from django.shortcuts import get_object_or_404, render, redirect +from django.urls import reverse +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.contexts import BaseContext +from konova.decorators import shared_access_required, default_group_required, any_group_check +from konova.forms import SimpleGeomForm +from konova.forms.modals import RemoveModalForm +from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP +from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER +from konova.utils.message_templates import COMPENSATION_REMOVED_TEMPLATE, DATA_CHECKED_PREVIOUSLY_TEMPLATE, \ + RECORDED_BLOCKS_EDIT, CHECKED_RECORDED_RESET, FORM_INVALID, PARAMS_INVALID, IDENTIFIER_REPLACED, \ + COMPENSATION_ADDED_TEMPLATE +from konova.utils.user_checks import in_group + + +@login_required +@any_group_check +def index_view(request: HttpRequest): + """ + Renders the index view for compensation + + Args: + request (HttpRequest): The incoming request + + Returns: + A rendered view + """ + template = "generic_index.html" + compensations = Compensation.objects.filter( + deleted=None, # only show those which are not deleted individually + intervention__deleted=None, # and don't show the ones whose intervention has been deleted + ) + table = CompensationTable( + request=request, + queryset=compensations + ) + context = { + "table": table, + TAB_TITLE_IDENTIFIER: _("Compensations - Overview"), + } + context = BaseContext(request, context).context + return render(request, template, context) + + +@login_required +@default_group_required +@shared_access_required(Intervention, "intervention_id") +def new_view(request: HttpRequest, intervention_id: str = None): + """ + Renders a view for a new compensation creation + + Args: + request (HttpRequest): The incoming request + + Returns: + + """ + template = "compensation/form/view.html" + if intervention_id is not None: + try: + intervention = Intervention.objects.get(id=intervention_id) + except ObjectDoesNotExist: + messages.error(request, PARAMS_INVALID) + return redirect("home") + if intervention.is_recorded: + messages.info( + request, + RECORDED_BLOCKS_EDIT + ) + return redirect("intervention:detail", id=intervention_id) + + data_form = NewCompensationForm(request.POST or None, intervention_id=intervention_id) + geom_form = SimpleGeomForm(request.POST or None, read_only=False) + if request.method == "POST": + if data_form.is_valid() and geom_form.is_valid(): + generated_identifier = data_form.cleaned_data.get("identifier", None) + comp = data_form.save(request.user, geom_form) + if generated_identifier != comp.identifier: + messages.info( + request, + IDENTIFIER_REPLACED.format( + generated_identifier, + comp.identifier + ) + ) + messages.success(request, COMPENSATION_ADDED_TEMPLATE.format(comp.identifier)) + return redirect("compensation:detail", id=comp.id) + else: + messages.error(request, FORM_INVALID, extra_tags="danger",) + else: + # For clarification: nothing in this case + pass + context = { + "form": data_form, + "geom_form": geom_form, + TAB_TITLE_IDENTIFIER: _("New compensation"), + } + context = BaseContext(request, context).context + return render(request, template, context) + + +@login_required +@default_group_required +def new_id_view(request: HttpRequest): + """ JSON endpoint + + Provides fetching of free identifiers for e.g. AJAX calls + + """ + tmp = Compensation() + identifier = tmp.generate_new_identifier() + while Compensation.objects.filter(identifier=identifier).exists(): + identifier = tmp.generate_new_identifier() + return JsonResponse( + data={ + "gen_data": identifier + } + ) + + +@login_required +@default_group_required +@shared_access_required(Compensation, "id") +def edit_view(request: HttpRequest, id: str): + """ + Renders a view for editing compensations + + Args: + request (HttpRequest): The incoming request + + Returns: + + """ + template = "compensation/form/view.html" + # Get object from db + comp = get_object_or_404(Compensation, id=id) + if comp.is_recorded: + messages.info( + request, + RECORDED_BLOCKS_EDIT + ) + return redirect("compensation:detail", id=id) + + # Create forms, initialize with values from db/from POST request + data_form = EditCompensationForm(request.POST or None, instance=comp) + geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=comp) + if request.method == "POST": + if data_form.is_valid() and geom_form.is_valid(): + # Preserve state of intervention recorded/checked to determine whether the user must be informed or not + # about a change of the recorded/checked state + intervention_recorded = comp.intervention.recorded is not None + intervention_checked = comp.intervention.checked is not None + + # The data form takes the geom form for processing, as well as the performing user + comp = data_form.save(request.user, geom_form) + if intervention_recorded or intervention_checked: + messages.info(request, CHECKED_RECORDED_RESET) + messages.success(request, _("Compensation {} edited").format(comp.identifier)) + return redirect("compensation:detail", id=comp.id) + else: + messages.error(request, FORM_INVALID, extra_tags="danger",) + else: + # For clarification: nothing in this case + pass + context = { + "form": data_form, + "geom_form": geom_form, + TAB_TITLE_IDENTIFIER: _("Edit {}").format(comp.identifier), + } + context = BaseContext(request, context).context + return render(request, template, context) + + +@login_required +@any_group_check +def detail_view(request: HttpRequest, id: str): + """ Renders a detail view for a compensation + + Args: + request (HttpRequest): The incoming request + id (str): The compensation's id + + Returns: + + """ + template = "compensation/detail/compensation/view.html" + comp = get_object_or_404(Compensation, id=id) + geom_form = SimpleGeomForm(instance=comp) + parcels = comp.get_underlying_parcels() + _user = request.user + is_data_shared = comp.intervention.is_shared_with(_user) + + # Order states according to surface + before_states = comp.before_states.all().prefetch_related("biotope_type").order_by("-surface") + after_states = comp.after_states.all().prefetch_related("biotope_type").order_by("-surface") + actions = comp.actions.all().prefetch_related("action_type") + + # Precalculate logical errors between before- and after-states + # Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling + sum_before_states = before_states.aggregate(Sum("surface"))["surface__sum"] or 0 + sum_after_states = after_states.aggregate(Sum("surface"))["surface__sum"] or 0 + diff_states = abs(sum_before_states - sum_after_states) + + request = comp.set_status_messages(request) + + last_checked = comp.intervention.get_last_checked_action() + last_checked_tooltip = "" + if last_checked: + last_checked_tooltip = DATA_CHECKED_PREVIOUSLY_TEMPLATE.format(last_checked.get_timestamp_str_formatted(), last_checked.user) + + context = { + "obj": comp, + "last_checked": last_checked, + "last_checked_tooltip": last_checked_tooltip, + "geom_form": geom_form, + "parcels": parcels, + "has_access": is_data_shared, + "actions": actions, + "before_states": before_states, + "after_states": after_states, + "sum_before_states": sum_before_states, + "sum_after_states": sum_after_states, + "diff_states": diff_states, + "is_default_member": in_group(_user, DEFAULT_GROUP), + "is_zb_member": in_group(_user, ZB_GROUP), + "is_ets_member": in_group(_user, ETS_GROUP), + "LANIS_LINK": comp.get_LANIS_link(), + TAB_TITLE_IDENTIFIER: f"{comp.identifier} - {comp.title}", + "has_finished_deadlines": comp.get_finished_deadlines().exists(), + } + context = BaseContext(request, context).context + return render(request, template, context) + + +@login_required +@default_group_required +@shared_access_required(Compensation, "id") +def remove_view(request: HttpRequest, id: str): + """ Renders a modal view for removing the compensation + + Args: + request (HttpRequest): The incoming request + id (str): The compensation's id + + Returns: + + """ + comp = get_object_or_404(Compensation, id=id) + form = RemoveModalForm(request.POST or None, instance=comp, request=request) + return form.process_request( + request=request, + msg_success=COMPENSATION_REMOVED_TEMPLATE.format(comp.identifier), + redirect_url=reverse("compensation:index"), + ) + diff --git a/compensation/views/compensation/deadline.py b/compensation/views/compensation/deadline.py new file mode 100644 index 0000000..ee6a512 --- /dev/null +++ b/compensation/views/compensation/deadline.py @@ -0,0 +1,89 @@ +""" +Author: Michel Peltriaux +Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany +Contact: ksp-servicestelle@sgdnord.rlp.de +Created on: 19.08.22 + +""" +from django.contrib.auth.decorators import login_required +from django.http import HttpRequest +from django.shortcuts import get_object_or_404 +from django.urls import reverse + +from compensation.forms.modals.deadline import EditDeadlineModalForm, NewDeadlineModalForm +from compensation.models import Compensation +from konova.decorators import shared_access_required, default_group_required +from konova.forms.modals import RemoveDeadlineModalForm +from konova.models import Deadline +from konova.utils.message_templates import DEADLINE_REMOVED, DEADLINE_EDITED, DEADLINE_ADDED + + +@login_required +@default_group_required +@shared_access_required(Compensation, "id") +def deadline_new_view(request: HttpRequest, id: str): + """ Renders a form for adding new states for a compensation + + Args: + request (HttpRequest): The incoming request + id (str): The compensation's id to which the new state will be related + + Returns: + + """ + comp = get_object_or_404(Compensation, id=id) + form = NewDeadlineModalForm(request.POST or None, instance=comp, request=request) + return form.process_request( + request, + msg_success=DEADLINE_ADDED, + redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data" + ) + + +@login_required +@default_group_required +@shared_access_required(Compensation, "id") +def deadline_edit_view(request: HttpRequest, id: str, deadline_id: str): + """ Renders a form for editing deadlines from a compensation + + Args: + request (HttpRequest): The incoming request + id (str): The compensation's id + deadline_id (str): The deadline's id + + Returns: + + """ + comp = get_object_or_404(Compensation, id=id) + deadline = get_object_or_404(Deadline, id=deadline_id) + form = EditDeadlineModalForm(request.POST or None, instance=comp, deadline=deadline, request=request) + return form.process_request( + request, + msg_success=DEADLINE_EDITED, + redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data" + ) + + +@login_required +@default_group_required +@shared_access_required(Compensation, "id") +def deadline_remove_view(request: HttpRequest, id: str, deadline_id: str): + """ Renders a form for removing deadlines from a compensation + + Args: + request (HttpRequest): The incoming request + id (str): The compensation's id + deadline_id (str): The deadline's id + + Returns: + + """ + comp = get_object_or_404(Compensation, id=id) + deadline = get_object_or_404(Deadline, id=deadline_id) + form = RemoveDeadlineModalForm(request.POST or None, instance=comp, deadline=deadline, request=request) + return form.process_request( + request, + msg_success=DEADLINE_REMOVED, + redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data" + ) + diff --git a/compensation/views/compensation/document.py b/compensation/views/compensation/document.py new file mode 100644 index 0000000..8ca99ed --- /dev/null +++ b/compensation/views/compensation/document.py @@ -0,0 +1,111 @@ +""" +Author: Michel Peltriaux +Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany +Contact: ksp-servicestelle@sgdnord.rlp.de +Created on: 19.08.22 + +""" +from django.contrib.auth.decorators import login_required +from django.http import HttpRequest +from django.shortcuts import get_object_or_404 +from django.urls import reverse + +from compensation.forms.modals.document import NewCompensationDocumentModalForm +from compensation.models import Compensation, CompensationDocument +from konova.decorators import shared_access_required, default_group_required +from konova.forms.modals import EditDocumentModalForm +from konova.utils.documents import remove_document, get_document +from konova.utils.message_templates import DOCUMENT_EDITED, DOCUMENT_ADDED + + +@login_required +@default_group_required +@shared_access_required(Compensation, "id") +def new_document_view(request: HttpRequest, id: str): + """ Renders a form for uploading new documents + + Args: + request (HttpRequest): The incoming request + id (str): The compensation's id to which the new document will be related + Returns: + + """ + comp = get_object_or_404(Compensation, id=id) + form = NewCompensationDocumentModalForm(request.POST or None, request.FILES or None, instance=comp, request=request) + return form.process_request( + request, + msg_success=DOCUMENT_ADDED, + redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data" + ) + + +@login_required +@default_group_required +@shared_access_required(Compensation, "id") +def get_document_view(request: HttpRequest, id: str, doc_id: str): + """ Returns the document as downloadable file + + Wraps the generic document fetcher function from konova.utils. + + Args: + request (HttpRequest): The incoming request + id (str): The compensation id + doc_id (str): The document id + + Returns: + + """ + comp = get_object_or_404(Compensation, id=id) + doc = get_object_or_404(CompensationDocument, id=doc_id) + return get_document(doc) + + +@login_required +@default_group_required +@shared_access_required(Compensation, "id") +def remove_document_view(request: HttpRequest, id: str, doc_id: str): + """ Removes the document from the database and file system + + Wraps the generic functionality from konova.utils. + + Args: + request (HttpRequest): The incoming request + id (str): The compensation id + doc_id (str): The document id + + Returns: + + """ + comp = get_object_or_404(Compensation, id=id) + doc = get_object_or_404(CompensationDocument, id=doc_id) + return remove_document( + request, + doc + ) + + +@login_required +@default_group_required +@shared_access_required(Compensation, "id") +def edit_document_view(request: HttpRequest, id: str, doc_id: str): + """ Removes the document from the database and file system + + Wraps the generic functionality from konova.utils. + + Args: + request (HttpRequest): The incoming request + id (str): The compensation id + doc_id (str): The document id + + Returns: + + """ + comp = get_object_or_404(Compensation, id=id) + doc = get_object_or_404(CompensationDocument, id=doc_id) + form = EditDocumentModalForm(request.POST or None, request.FILES or None, instance=comp, document=doc, request=request) + return form.process_request( + request, + DOCUMENT_EDITED, + reverse("compensation:detail", args=(id,)) + "#related_data" + ) + diff --git a/compensation/views/compensation/log.py b/compensation/views/compensation/log.py new file mode 100644 index 0000000..91b38aa --- /dev/null +++ b/compensation/views/compensation/log.py @@ -0,0 +1,41 @@ +""" +Author: Michel Peltriaux +Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany +Contact: ksp-servicestelle@sgdnord.rlp.de +Created on: 19.08.22 + +""" +from django.contrib.auth.decorators import login_required +from django.http import HttpRequest +from django.shortcuts import get_object_or_404, render +from django.utils.translation import gettext_lazy as _ + +from compensation.models import Compensation +from konova.contexts import BaseContext +from konova.decorators import shared_access_required, default_group_required + + +@login_required +@default_group_required +@shared_access_required(Compensation, "id") +def log_view(request: HttpRequest, id: str): + """ Renders a log view using modal + + Args: + request (HttpRequest): The incoming request + id (str): The compensation's id + + Returns: + + """ + comp = get_object_or_404(Compensation, id=id) + template = "modal/modal_generic.html" + body_template = "log.html" + + context = { + "modal_body_template": body_template, + "log": comp.log.all(), + "modal_title": _("Log"), + } + context = BaseContext(request, context).context + return render(request, template, context) diff --git a/compensation/views/compensation/report.py b/compensation/views/compensation/report.py new file mode 100644 index 0000000..28d563e --- /dev/null +++ b/compensation/views/compensation/report.py @@ -0,0 +1,79 @@ +""" +Author: Michel Peltriaux +Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany +Contact: ksp-servicestelle@sgdnord.rlp.de +Created on: 19.08.22 + +""" +from django.http import HttpRequest +from django.shortcuts import get_object_or_404, render +from django.urls import reverse +from django.utils.translation import gettext_lazy as _ + +from compensation.models import Compensation +from konova.contexts import BaseContext +from konova.forms import SimpleGeomForm +from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER +from konova.utils.generators import generate_qr_code + + +def report_view(request: HttpRequest, id: str): + """ Renders the public report view + + Args: + request (HttpRequest): The incoming request + id (str): The id of the intervention + + Returns: + + """ + # Reuse the compensation report template since compensations are structurally identical + template = "compensation/report/compensation/report.html" + comp = get_object_or_404(Compensation, id=id) + + tab_title = _("Report {}").format(comp.identifier) + # If intervention is not recorded (yet or currently) we need to render another template without any data + if not comp.is_ready_for_publish(): + template = "report/unavailable.html" + context = { + TAB_TITLE_IDENTIFIER: tab_title, + } + context = BaseContext(request, context).context + return render(request, template, context) + + # Prepare data for map viewer + geom_form = SimpleGeomForm( + instance=comp + ) + parcels = comp.get_underlying_parcels() + + qrcode_url = request.build_absolute_uri(reverse("compensation:report", args=(id,))) + qrcode_img = generate_qr_code(qrcode_url, 10) + qrcode_lanis_url = comp.get_LANIS_link() + qrcode_img_lanis = generate_qr_code(qrcode_lanis_url, 7) + + # Order states by surface + before_states = comp.before_states.all().order_by("-surface").prefetch_related("biotope_type") + after_states = comp.after_states.all().order_by("-surface").prefetch_related("biotope_type") + actions = comp.actions.all().prefetch_related("action_type") + + context = { + "obj": comp, + "qrcode": { + "img": qrcode_img, + "url": qrcode_url, + }, + "qrcode_lanis": { + "img": qrcode_img_lanis, + "url": qrcode_lanis_url, + }, + "has_access": False, # disables action buttons during rendering + "before_states": before_states, + "after_states": after_states, + "geom_form": geom_form, + "parcels": parcels, + "actions": actions, + TAB_TITLE_IDENTIFIER: tab_title, + } + context = BaseContext(request, context).context + return render(request, template, context) diff --git a/compensation/views/compensation/resubmission.py b/compensation/views/compensation/resubmission.py new file mode 100644 index 0000000..d90a91c --- /dev/null +++ b/compensation/views/compensation/resubmission.py @@ -0,0 +1,39 @@ +""" +Author: Michel Peltriaux +Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany +Contact: ksp-servicestelle@sgdnord.rlp.de +Created on: 19.08.22 + +""" +from django.contrib.auth.decorators import login_required +from django.http import HttpRequest +from django.shortcuts import get_object_or_404 +from django.urls import reverse +from django.utils.translation import gettext_lazy as _ + +from compensation.models import Compensation +from konova.decorators import shared_access_required, default_group_required +from konova.forms.modals import ResubmissionModalForm + + +@login_required +@default_group_required +@shared_access_required(Compensation, "id") +def create_resubmission_view(request: HttpRequest, id: str): + """ Renders resubmission form for a compensation + + Args: + request (HttpRequest): The incoming request + id (str): Compensation's id + + Returns: + + """ + com = get_object_or_404(Compensation, id=id) + form = ResubmissionModalForm(request.POST or None, instance=com, request=request) + form.action_url = reverse("compensation:resubmission-create", args=(id,)) + return form.process_request( + request, + msg_success=_("Resubmission set"), + redirect_url=reverse("compensation:detail", args=(id,)) + ) diff --git a/compensation/views/compensation/state.py b/compensation/views/compensation/state.py new file mode 100644 index 0000000..ca8c5d0 --- /dev/null +++ b/compensation/views/compensation/state.py @@ -0,0 +1,89 @@ +""" +Author: Michel Peltriaux +Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany +Contact: ksp-servicestelle@sgdnord.rlp.de +Created on: 19.08.22 + +""" +from django.contrib.auth.decorators import login_required +from django.http import HttpRequest +from django.shortcuts import get_object_or_404 +from django.urls import reverse + +from compensation.forms.modals.state import EditCompensationStateModalForm, RemoveCompensationStateModalForm, \ + NewCompensationStateModalForm +from compensation.models import Compensation, CompensationState +from konova.decorators import shared_access_required, default_group_required +from konova.utils.message_templates import COMPENSATION_STATE_EDITED, COMPENSATION_STATE_REMOVED, \ + COMPENSATION_STATE_ADDED + + +@login_required +@default_group_required +@shared_access_required(Compensation, "id") +def state_new_view(request: HttpRequest, id: str): + """ Renders a form for adding new states for a compensation + + Args: + request (HttpRequest): The incoming request + id (str): The compensation's id to which the new state will be related + + Returns: + + """ + comp = get_object_or_404(Compensation, id=id) + form = NewCompensationStateModalForm(request.POST or None, instance=comp, request=request) + return form.process_request( + request, + msg_success=COMPENSATION_STATE_ADDED, + redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data" + ) + + +@login_required +@default_group_required +@shared_access_required(Compensation, "id") +def state_remove_view(request: HttpRequest, id: str, state_id: str): + """ Renders a form for removing a compensation state + + Args: + request (HttpRequest): The incoming request + id (str): The compensation's id + state_id (str): The state's id + + Returns: + + """ + comp = get_object_or_404(Compensation, id=id) + state = get_object_or_404(CompensationState, id=state_id) + form = RemoveCompensationStateModalForm(request.POST or None, instance=comp, state=state, request=request) + return form.process_request( + request, + msg_success=COMPENSATION_STATE_REMOVED, + redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data" + ) + + +@login_required +@default_group_required +@shared_access_required(Compensation, "id") +def state_edit_view(request: HttpRequest, id: str, state_id: str): + """ Renders a form for editing a compensation state + + Args: + request (HttpRequest): The incoming request + id (str): The compensation's id + state_id (str): The state's id + + Returns: + + """ + comp = get_object_or_404(Compensation, id=id) + state = get_object_or_404(CompensationState, id=state_id) + form = EditCompensationStateModalForm(request.POST or None, instance=comp, state=state, request=request) + return form.process_request( + request, + msg_success=COMPENSATION_STATE_EDITED, + redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data" + ) +