You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
konova/compensation/models.py

249 lines
8.0 KiB
Python

3 years ago
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 17.11.20
"""
from django.contrib.auth.models import User
3 years ago
from django.contrib.gis.db import models
from django.core.validators import MinValueValidator, MaxValueValidator
from django.db import transaction
from django.utils import timezone
3 years ago
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
3 years ago
from compensation.settings import COMPENSATION_IDENTIFIER_LENGTH, COMPENSATION_IDENTIFIER_TEMPLATE
from intervention.models import Intervention, ResponsibilityData
from konova.models import BaseObject, BaseResource, Geometry, UuidModel
3 years ago
from konova.utils.generators import generate_random_string
from organisation.models import Organisation
from user.models import UserActionLogEntry, UserAction
3 years ago
class Payment(BaseResource):
"""
Holds data on a payment for an intervention (alternative to a classic compensation)
"""
amount = models.FloatField(validators=[MinValueValidator(limit_value=0.00)])
due_on = models.DateField(null=True)
comment = models.CharField(
max_length=1000,
null=True,
blank=True,
help_text="Refers to german money transfer 'Verwendungszweck'",
)
intervention = models.ForeignKey(
Intervention,
null=True,
blank=True,
on_delete=models.CASCADE,
related_name='payments'
)
3 years ago
class CompensationControl(BaseResource):
"""
Holds data on how a compensation shall be controlled
"""
deadline = models.ForeignKey("konova.Deadline", on_delete=models.SET_NULL, null=True, blank=True)
type = models.CharField(max_length=500, null=True, blank=True)
expected_result = models.CharField(max_length=500, null=True, blank=True, help_text="The expected outcome, that needs to be controlled")
by_authority = models.ForeignKey(Organisation, null=True, blank=True, on_delete=models.SET_NULL)
3 years ago
comment = models.TextField()
class CompensationState(UuidModel):
3 years ago
"""
Compensations must define the state of an area before and after the compensation.
"""
biotope_type = models.CharField(max_length=500, null=True, blank=True)
surface = models.FloatField()
3 years ago
def __str__(self):
return "{} | {}".format(self.biotope_type, self.surface)
3 years ago
class UnitChoices(models.TextChoices):
"""
Predefines units for selection
"""
cm = "cm", _("cm")
m = "m", _("m")
km = "km", _("km")
qm = "qm", _("")
ha = "ha", _("ha")
st = "pcs", _("Pieces") # pieces
3 years ago
class CompensationAction(BaseResource):
"""
Compensations include actions like planting trees, refreshing rivers and so on.
"""
action_type = models.CharField(max_length=500, null=True, blank=True)
amount = models.FloatField()
unit = models.CharField(max_length=100, null=True, blank=True, choices=UnitChoices.choices)
3 years ago
control = models.ForeignKey(CompensationControl, on_delete=models.SET_NULL, null=True, blank=True)
def __str__(self):
return "{} | {} {}".format(self.action_type, self.amount, self.unit)
@property
def unit_humanize(self):
""" Returns humanized version of enum
Used for template rendering
Returns:
"""
choices = UnitChoices.choices
for choice in choices:
if choice[0] == self.unit:
return choice[1]
return None
3 years ago
class AbstractCompensation(BaseObject):
3 years ago
"""
Abstract compensation model which holds basic attributes, shared by subclasses like the regular Compensation
or EcoAccount.
3 years ago
"""
responsible = models.OneToOneField(
ResponsibilityData,
on_delete=models.SET_NULL,
null=True,
blank=True,
help_text="Holds data on responsible organizations ('Zulassungsbehörde', 'Eintragungsstelle') and handler",
)
before_states = models.ManyToManyField(CompensationState, blank=True, related_name='+', help_text="Refers to 'Ausgangszustand Biotop'")
after_states = models.ManyToManyField(CompensationState, blank=True, related_name='+', help_text="Refers to 'Zielzustand Biotop'")
actions = models.ManyToManyField(CompensationAction, blank=True, help_text="Refers to 'Maßnahmen'")
deadlines = models.ManyToManyField("konova.Deadline", null=True, blank=True, related_name="+")
3 years ago
geometry = models.ForeignKey(Geometry, null=True, blank=True, on_delete=models.SET_NULL)
documents = models.ManyToManyField("konova.Document", blank=True)
3 years ago
# Holds which intervention is simply a newer version of this dataset
next_version = models.ForeignKey("Compensation", null=True, blank=True, on_delete=models.DO_NOTHING)
class Meta:
abstract = True
class Compensation(AbstractCompensation):
"""
Regular compensation, linked to an intervention
"""
intervention = models.ForeignKey(
Intervention,
on_delete=models.CASCADE,
null=True,
blank=True,
related_name='compensations'
)
def __str__(self):
return "{}".format(self.identifier)
3 years ago
@staticmethod
def _generate_new_identifier() -> str:
3 years ago
""" Generates a new identifier for the intervention object
Returns:
str
"""
curr_month = str(now().month)
curr_year = str(now().year)
rand_str = generate_random_string(
length=COMPENSATION_IDENTIFIER_LENGTH,
only_numbers=True,
)
_str = "{}{}{}".format(curr_month, curr_year, rand_str)
return COMPENSATION_IDENTIFIER_TEMPLATE.format(_str)
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:
"""
_now = timezone.now()
_user = kwargs.get("user", None)
with transaction.atomic():
action = UserActionLogEntry.objects.create(
user=_user,
timestamp=_now,
action=UserAction.DELETED
)
self.deleted = action
self.save()
3 years ago
def save(self, *args, **kwargs):
if self.identifier is None or len(self.identifier) == 0:
# Create new identifier
new_id = self._generate_new_identifier()
3 years ago
while Compensation.objects.filter(identifier=new_id).exists():
new_id = self._generate_new_identifier()
3 years ago
self.identifier = new_id
super().save(*args, **kwargs)
class EcoAccount(AbstractCompensation):
3 years ago
"""
An eco account is a kind of 'prepaid' compensation. It can be compared to an account that already has been filled
with some kind of currency. From this account one is able to 'withdraw' currency for current projects.
"""
# Users having access on this object
# Not needed in regular Compensation since their access is defined by the linked intervention's access
users = models.ManyToManyField(
User,
help_text="Users having access"
)
def __str__(self):
return "{}".format(self.identifier)
class EcoAccountWithdraw(BaseResource):
"""
A withdraw object for eco accounts
"""
account = models.ForeignKey(
EcoAccount,
on_delete=models.SET_NULL,
null=True,
blank=True,
help_text="Withdrawn from",
related_name="eco_withdraws",
)
amount = models.FloatField(
null=True,
blank=True,
help_text="Amount withdrawn (percentage)",
validators=[
MinValueValidator(limit_value=0.00),
MaxValueValidator(limit_value=100),
]
)
intervention = models.ForeignKey(
Intervention,
on_delete=models.CASCADE,
null=True,
blank=True,
help_text="Withdrawn for",
related_name="eco_withdraws",
)
def __str__(self):
return "{} of {}".format(self.amount, self.account)