""" Author: Michel Peltriaux Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany Contact: michel.peltriaux@sgdnord.rlp.de Created on: 17.11.20 """ import os import uuid from django.contrib.auth.models import User from django.utils.timezone import now from django.utils.translation import gettext_lazy as _ from django.contrib.gis.db.models import MultiPolygonField from django.db import models, transaction from compensation.settings import COMPENSATION_IDENTIFIER_TEMPLATE, COMPENSATION_IDENTIFIER_LENGTH, \ ECO_ACCOUNT_IDENTIFIER_TEMPLATE, ECO_ACCOUNT_IDENTIFIER_LENGTH from ema.settings import EMA_ACCOUNT_IDENTIFIER_LENGTH, EMA_ACCOUNT_IDENTIFIER_TEMPLATE from intervention.settings import INTERVENTION_IDENTIFIER_LENGTH, INTERVENTION_IDENTIFIER_TEMPLATE from konova.utils.generators import generate_random_string from user.models import UserActionLogEntry, UserAction class UuidModel(models.Model): """ Encapsules identifying via uuid """ id = models.UUIDField( primary_key=True, default=uuid.uuid4, editable=False, ) class Meta: abstract = True class BaseResource(UuidModel): """ A basic resource model, which defines attributes for every derived model """ created = models.ForeignKey( UserActionLogEntry, on_delete=models.SET_NULL, null=True, blank=True, related_name='+' ) modified = models.ForeignKey( UserActionLogEntry, on_delete=models.SET_NULL, null=True, blank=True, related_name='+', help_text="Last modified" ) class Meta: abstract = True def delete(self, using=None, keep_parents=False): if self.created: self.created.delete() super().delete() class BaseObject(BaseResource): """ A basic object model, which specifies BaseResource. Mainly used for intervention, compensation, ecoaccount """ identifier = models.CharField(max_length=1000, null=True, blank=True) title = models.CharField(max_length=1000, null=True, blank=True) deleted = models.ForeignKey(UserActionLogEntry, on_delete=models.SET_NULL, null=True, blank=True, related_name='+') comment = models.TextField(null=True, blank=True) log = models.ManyToManyField(UserActionLogEntry, blank=True, help_text="Keeps all user actions of an object", editable=False) class Meta: abstract = True def delete(self, *args, **kwargs): """ Custom delete functionality Does not delete from database but sets a timestamp for being deleted on and which user deleted the object Args: *args (): **kwargs (): Returns: """ if self.deleted: # Nothing to do here return _user = kwargs.get("user", None) with transaction.atomic(): action = UserActionLogEntry.objects.create( user=_user, action=UserAction.DELETED ) self.deleted = action self.save() def add_log_entry(self, action: UserAction, user: User, comment: str): """ Wraps adding of UserActionLogEntry to self.log Args: action (UserAction): The performed UserAction user (User): Performing user Returns: """ user_action = UserActionLogEntry.objects.create( user=user, action=action, comment=comment ) self.log.add(user_action) def is_shared_with(self, user: User): """ Access check Checks whether a given user has access to this object Args: user (): Returns: """ if hasattr(self, "users"): return self.users.filter(username=user.username).exists() else: return User.objects.none() def _generate_new_identifier(self) -> str: """ Generates a new identifier for the intervention object Returns: str """ from compensation.models import Compensation, EcoAccount from intervention.models import Intervention from ema.models import Ema definitions = { Intervention: { "length": INTERVENTION_IDENTIFIER_LENGTH, "template": INTERVENTION_IDENTIFIER_TEMPLATE, }, Compensation: { "length": COMPENSATION_IDENTIFIER_LENGTH, "template": COMPENSATION_IDENTIFIER_TEMPLATE, }, EcoAccount: { "length": ECO_ACCOUNT_IDENTIFIER_LENGTH, "template": ECO_ACCOUNT_IDENTIFIER_TEMPLATE, }, Ema: { "length": EMA_ACCOUNT_IDENTIFIER_LENGTH, "template": EMA_ACCOUNT_IDENTIFIER_TEMPLATE, }, } if self.__class__ not in definitions: # Not defined, yet. Create fallback identifier for this case return generate_random_string(10) _now = now() curr_month = str(_now.month) curr_year = str(_now.year) rand_str = generate_random_string( length=definitions[self.__class__]["length"], only_numbers=True, ) _str = "{}{}-{}".format(curr_month, curr_year, rand_str) return definitions[self.__class__]["template"].format(_str) class DeadlineType(models.TextChoices): """ Django 3.x way of handling enums for models """ FINISHED = "finished", _("Finished") MAINTAIN = "maintain", _("Maintain") CONTROL = "control", _("Control") OTHER = "other", _("Other") class Deadline(BaseResource): """ Defines a deadline, which can be used to define dates with a semantic meaning """ type = models.CharField(max_length=255, null=True, blank=True, choices=DeadlineType.choices) date = models.DateField(null=True, blank=True) comment = models.CharField(max_length=1000, null=True, blank=True) def __str__(self): return self.type @property def type_humanized(self): """ Returns humanized version of enum Used for template rendering Returns: """ choices = DeadlineType.choices for choice in choices: if choice[0] == self.type: return choice[1] return None class Document(BaseResource): """ Documents can be attached to compensation or intervention for uploading legal documents or pictures. """ title = models.CharField(max_length=500, null=True, blank=True) date_of_creation = models.DateField() file = models.FileField() comment = models.TextField() def delete(self, using=None, keep_parents=False): """ Custom delete function to remove the real file from the hard drive Args: using (): keep_parents (): Returns: """ os.remove(self.file.file.name) super().delete(using=using, keep_parents=keep_parents) class Geometry(BaseResource): """ Outsourced geometry model so multiple versions of the same object can refer to the same geometry if it is not changed """ from konova.settings import DEFAULT_SRID geom = MultiPolygonField(null=True, blank=True, srid=DEFAULT_SRID)