""" Author: Michel Peltriaux Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany Contact: michel.peltriaux@sgdnord.rlp.de Created on: 15.11.21 """ import shutil from django.contrib import messages from django.db import models from django.db.models import QuerySet from django.http import HttpRequest from django.urls import reverse from compensation.models import AbstractCompensation, PikMixin from ema.managers import EmaManager from ema.settings import EMA_IDENTIFIER_LENGTH, EMA_IDENTIFIER_TEMPLATE, EMA_LANIS_LAYER_NAME_RECORDED, \ EMA_LANIS_LAYER_NAME_UNRECORDED from ema.utils.quality import EmaQualityChecker from konova.models import AbstractDocument, generate_document_file_upload_path, RecordableObjectMixin, ShareableObjectMixin from konova.sub_settings.django_settings import BASE_URL from konova.utils.message_templates import DATA_UNSHARED_EXPLANATION, DOCUMENT_REMOVED_TEMPLATE class Ema(AbstractCompensation, ShareableObjectMixin, RecordableObjectMixin, PikMixin): """ EMA = Ersatzzahlungsmaßnahme (compensation actions from payments) Until 2015 the EMA was the data object to keep track of any compensation, which has been funded by payments previously paid. In 2015 another organization got in charge of this, which led to the creation of the data object MAE (which is basically the same, just renamed in their system) to differ between the 'old' payment funded ones and the new. For historical reasons, we need to keep EMAs in our system, since there are still entries done to this day, which have been performed somewhere before 2015 and therefore needs to be entered. Further information: https://snu.rlp.de/de/foerderungen/massnahmen-aus-ersatzzahlungen/uebersicht-mae/ EMA therefore holds data like a compensation: actions, before-/after-states, deadlines, ... """ objects = EmaManager() identifier_length = EMA_IDENTIFIER_LENGTH identifier_template = EMA_IDENTIFIER_TEMPLATE def __str__(self): return "{}".format(self.identifier) def get_detail_url(self): return reverse("ema:detail", args=(self.id,)) def get_detail_url_absolute(self): return BASE_URL + self.get_detail_url() def save(self, *args, **kwargs): if self.identifier is None or len(self.identifier) == 0: # Create new identifier new_id = self.generate_new_identifier() while Ema.objects.filter(identifier=new_id).exists(): new_id = self.generate_new_identifier() self.identifier = new_id super().save(*args, **kwargs) def quality_check(self) -> EmaQualityChecker: """ Quality check Returns: ret_msgs (EmaQualityChecker): Holds validity error messages """ checker = EmaQualityChecker(self) checker.run_check() return checker def get_documents(self) -> QuerySet: """ Getter for all documents of an EMA Returns: docs (QuerySet): The queryset of all documents """ docs = EmaDocument.objects.filter( instance=self ) return docs def set_status_messages(self, request: HttpRequest): """ Setter for different information that need to be rendered Adds messages to the given HttpRequest Args: request (HttpRequest): The incoming request Returns: request (HttpRequest): The modified request """ if not self.is_shared_with(request.user): messages.info(request, DATA_UNSHARED_EXPLANATION) self.set_geometry_conflict_message(request) return request def is_ready_for_publish(self) -> bool: """ Checks whether the data passes all constraints for being publishable Returns: is_ready (bool) : True|False """ is_recorded = self.recorded is not None is_ready = is_recorded return is_ready def get_share_link(self): """ Returns the share url for the object Returns: """ return reverse("ema:share-token", args=(self.id, self.access_token)) def get_lanis_layer_name(self): """ Getter for specific LANIS/WFS object layer Returns: """ retval = None if self.is_recorded: retval = EMA_LANIS_LAYER_NAME_RECORDED else: retval = EMA_LANIS_LAYER_NAME_UNRECORDED return retval class EmaDocument(AbstractDocument): """ Specializes document upload for ema with certain path """ instance = models.ForeignKey( Ema, on_delete=models.CASCADE, related_name="documents", ) file = models.FileField( upload_to=generate_document_file_upload_path, max_length=1000, ) def delete(self, user=None, *args, **kwargs): """ Custom delete functionality for EmaDocuments. Removes the folder from the file system if there are no further documents for this entry. Args: *args (): **kwargs (): Returns: """ ema_docs = self.instance.get_documents() folder_path = None if ema_docs.count() == 1: # The only file left for this EMA is the one which is currently processed and will be deleted # Make sure that the compensation folder itself is deleted as well, not only the file # Therefore take the folder path from the file path try: folder_path = self.file.path.split("/")[:-1] folder_path = "/".join(folder_path) except ValueError: folder_path = None if user: self.instance.mark_as_edited(user, edit_comment=DOCUMENT_REMOVED_TEMPLATE.format(self.title)) # Remove the file itself super().delete(*args, **kwargs) # If a folder path has been set, we need to delete the whole folder! if folder_path is not None: try: shutil.rmtree(folder_path) except FileNotFoundError: # Folder seems to be missing already... pass