from django.contrib import messages from django.contrib.auth.decorators import login_required from django.db.models import Sum from django.http import HttpRequest, JsonResponse from django.shortcuts import render, get_object_or_404, redirect from django.urls import reverse from django.utils.translation import gettext_lazy as _ from compensation.forms.modalForms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm from compensation.models import CompensationAction, CompensationState from ema.forms import NewEmaForm, EditEmaForm, NewEmaDocumentForm from ema.tables import EmaTable from intervention.forms.modalForms import ShareModalForm from konova.contexts import BaseContext from konova.decorators import conservation_office_group_required, shared_access_required from ema.models import Ema, EmaDocument from konova.forms import RemoveModalForm, SimpleGeomForm, RecordModalForm from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP from konova.utils.documents import get_document, remove_document from konova.utils.generators import generate_qr_code from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID, DATA_UNSHARED, DATA_UNSHARED_EXPLANATION from konova.utils.user_checks import in_group @login_required def index_view(request: HttpRequest): """ Renders the index view for EMAs Args: request (HttpRequest): The incoming request Returns: """ template = "generic_index.html" emas = Ema.objects.filter( deleted=None, ).order_by( "-modified" ) table = EmaTable( request, queryset=emas ) context = { "table": table, } context = BaseContext(request, context).context return render(request, template, context) @login_required @conservation_office_group_required def new_view(request: HttpRequest): """ Renders a view for a new eco account creation Args: request (HttpRequest): The incoming request Returns: """ template = "ema/form/view.html" data_form = NewEmaForm(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) ema = data_form.save(request.user, geom_form) if generated_identifier != ema.identifier: messages.info( request, IDENTIFIER_REPLACED.format( generated_identifier, ema.identifier ) ) messages.success(request, _("EMA {} added").format(ema.identifier)) return redirect("ema:detail", id=ema.id) else: messages.error(request, FORM_INVALID, extra_tags="danger",) else: # For clarification: nothing in this case pass context = { "form": data_form, "geom_form": geom_form, } context = BaseContext(request, context).context return render(request, template, context) @login_required @conservation_office_group_required def new_id_view(request: HttpRequest): """ JSON endpoint Provides fetching of free identifiers for e.g. AJAX calls """ tmp = Ema() identifier = tmp.generate_new_identifier() while Ema.objects.filter(identifier=identifier).exists(): identifier = tmp.generate_new_identifier() return JsonResponse( data={ "identifier": identifier } ) @login_required def detail_view(request: HttpRequest, id: str): """ Renders the detail view of an EMA Args: request (HttpRequest): The incoming request id (str): The EMA id Returns: """ template = "ema/detail/view.html" ema = get_object_or_404(Ema, id=id, deleted=None) geom_form = SimpleGeomForm(instance=ema) _user = request.user is_data_shared = ema.is_shared_with(_user) # Order states according to surface before_states = ema.before_states.all().order_by("-surface") after_states = ema.after_states.all().order_by("-surface") # 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) ema.set_status_messages(request) context = { "obj": ema, "geom_form": geom_form, "has_access": is_data_shared, "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": ema.get_LANIS_link(), } context = BaseContext(request, context).context return render(request, template, context) @login_required @conservation_office_group_required @shared_access_required(Ema, "id") def log_view(request: HttpRequest, id: str): """ Renders a log view using modal Args: request (HttpRequest): The incoming request id (str): The EMA's id Returns: """ ema = get_object_or_404(Ema, id=id) template = "modal/modal_generic.html" body_template = "log.html" context = { "modal_body_template": body_template, "log": ema.log.all(), "modal_title": _("Log"), } context = BaseContext(request, context).context return render(request, template, context) @login_required @conservation_office_group_required @shared_access_required(Ema, "id") def edit_view(request: HttpRequest, id: str): """ Renders a view for editing compensations Args: request (HttpRequest): The incoming request Returns: """ template = "compensation/form/view.html" # Get object from db ema = get_object_or_404(Ema, id=id) # Create forms, initialize with values from db/from POST request data_form = EditEmaForm(request.POST or None, instance=ema) geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=ema) if request.method == "POST": if data_form.is_valid() and geom_form.is_valid(): # The data form takes the geom form for processing, as well as the performing user ema = data_form.save(request.user, geom_form) messages.success(request, _("EMA {} edited").format(ema.identifier)) return redirect("ema:detail", id=ema.id) else: messages.error(request, FORM_INVALID, extra_tags="danger",) else: # For clarification: nothing in this case pass context = { "form": data_form, "geom_form": geom_form, } context = BaseContext(request, context).context return render(request, template, context) @login_required @conservation_office_group_required @shared_access_required(Ema, "id") def remove_view(request: HttpRequest, id: str): """ Renders a modal view for removing the EMA Args: request (HttpRequest): The incoming request id (str): The EMA's id Returns: """ ema = get_object_or_404(Ema, id=id) form = RemoveModalForm(request.POST or None, instance=ema, request=request) return form.process_request( request=request, msg_success=_("EMA removed"), redirect_url=reverse("ema:index"), ) @login_required @conservation_office_group_required @shared_access_required(Ema, "id") def record_view(request: HttpRequest, id: str): """ Renders a modal view for recording the EMA Args: request (HttpRequest): The incoming request id (str): The EMA's id Returns: """ ema = get_object_or_404(Ema, id=id) msg_succ = _("{} unrecorded") if ema.recorded else _("{} recorded") form = RecordModalForm(request.POST or None, instance=ema, request=request) return form.process_request( request=request, msg_success=msg_succ.format("EMA"), ) @login_required @conservation_office_group_required @shared_access_required(Ema, "id") def state_new_view(request: HttpRequest, id: str): """ Renders a form for adding new states for an EMA Args: request (HttpRequest): The incoming request id (str): The EMA's id to which the new state will be related Returns: """ ema = get_object_or_404(Ema, id=id) form = NewStateModalForm(request.POST or None, instance=ema, request=request) return form.process_request( request, msg_success=_("State added") ) @login_required @conservation_office_group_required @shared_access_required(Ema, "id") def action_new_view(request: HttpRequest, id: str): """ Renders a form for adding new actions for an EMA Args: request (HttpRequest): The incoming request id (str): The EMA's id to which the new state will be related Returns: """ ema = get_object_or_404(Ema, id=id) form = NewActionModalForm(request.POST or None, instance=ema, request=request) return form.process_request( request, msg_success=_("Action added") ) @login_required @conservation_office_group_required @shared_access_required(Ema, "id") def deadline_new_view(request: HttpRequest, id: str): """ Renders a form for adding new states for an EMA Args: request (HttpRequest): The incoming request id (str): The EMA's id to which the new state will be related Returns: """ ema = get_object_or_404(Ema, id=id) form = NewDeadlineModalForm(request.POST or None, instance=ema, request=request) return form.process_request( request, msg_success=_("Deadline added") ) @login_required @conservation_office_group_required @shared_access_required(Ema, "id") def document_new_view(request: HttpRequest, id: str): """ Renders a form for uploading new documents Args: request (HttpRequest): The incoming request id (str): The EMA's id to which the new document will be related Returns: """ ema = get_object_or_404(Ema, id=id) form = NewEmaDocumentForm(request.POST or None, request.FILES or None, instance=ema, request=request) return form.process_request( request, msg_success=_("Document added") ) @login_required @conservation_office_group_required def get_document_view(request: HttpRequest, doc_id: str): """ Returns the document as downloadable file Wraps the generic document fetcher function from konova.utils. Args: request (HttpRequest): The incoming request doc_id (str): The document id Returns: """ doc = get_object_or_404(EmaDocument, id=doc_id) user = request.user instance = doc.instance # File download only possible if related instance is shared with user if not instance.users.filter(id=user.id): messages.info( request, DATA_UNSHARED ) return redirect("ema:detail", id=instance.id) return get_document(doc) @login_required @conservation_office_group_required def remove_document_view(request: HttpRequest, 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 doc_id (str): The document id Returns: """ doc = get_object_or_404(EmaDocument, id=doc_id) return remove_document( request, doc ) @login_required @conservation_office_group_required @shared_access_required(Ema, "id") def state_remove_view(request: HttpRequest, id: str, state_id: str): """ Renders a form for removing an EMA state Args: request (HttpRequest): The incoming request id (str): The ema id state_id (str): The state's id Returns: """ state = get_object_or_404(CompensationState, id=state_id) form = RemoveModalForm(request.POST or None, instance=state, request=request) return form.process_request( request, msg_success=_("State removed") ) @login_required @conservation_office_group_required @shared_access_required(Ema, "id") def action_remove_view(request: HttpRequest, id: str, action_id: str): """ Renders a form for removing an EMA action Args: request (HttpRequest): The incoming request id (str): The ema id id (str): The action's id Returns: """ action = get_object_or_404(CompensationAction, id=action_id) form = RemoveModalForm(request.POST or None, instance=action, request=request) return form.process_request( request, msg_success=_("Action removed") ) 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 EMAs are structurally identical template = "ema/report/report.html" ema = get_object_or_404(Ema, id=id) # If intervention is not recorded (yet or currently) we need to render another template without any data if not ema.recorded: template = "report/unavailable.html" return render(request, template, {}) # Prepare data for map viewer geom_form = SimpleGeomForm( instance=ema, ) qrcode_img = generate_qr_code( request.build_absolute_uri(reverse("ema:report", args=(id,))), 10 ) qrcode_img_lanis = generate_qr_code( ema.get_LANIS_link(), 7 ) # Order states by surface before_states = ema.before_states.all().order_by("-surface").prefetch_related("biotope_type") after_states = ema.after_states.all().order_by("-surface").prefetch_related("biotope_type") actions = ema.actions.all().prefetch_related("action_type") context = { "obj": ema, "qrcode": qrcode_img, "qrcode_lanis": qrcode_img_lanis, "has_access": False, # disables action buttons during rendering "before_states": before_states, "after_states": after_states, "geom_form": geom_form, "actions": actions, } context = BaseContext(request, context).context return render(request, template, context) @login_required def share_view(request: HttpRequest, id: str, token: str): """ Performs sharing of an ema If token given in url is not valid, the user will be redirected to the dashboard Args: request (HttpRequest): The incoming request id (str): EMA's id token (str): Access token for EMA Returns: """ user = request.user obj = get_object_or_404(Ema, id=id) # Check tokens if obj.access_token == token: # Send different messages in case user has already been added to list of sharing users if obj.is_shared_with(user): messages.info( request, _("{} has already been shared with you").format(obj.identifier) ) else: messages.success( request, _("{} has been shared with you").format(obj.identifier) ) obj.share_with(user) return redirect("ema:detail", id=id) else: messages.error( request, _("Share link invalid"), extra_tags="danger", ) return redirect("home") @login_required @conservation_office_group_required @shared_access_required(Ema, "id") def create_share_view(request: HttpRequest, id: str): """ Renders sharing form for an Ema Args: request (HttpRequest): The incoming request id (str): Ema's id Returns: """ obj = get_object_or_404(Ema, id=id) form = ShareModalForm(request.POST or None, instance=obj, request=request) return form.process_request( request, msg_success=_("Share settings updated") )