This commit is contained in:
mipel
2021-07-01 13:36:07 +02:00
commit a5e8bcfa8c
91 changed files with 22395 additions and 0 deletions

0
compensation/__init__.py Normal file
View File

46
compensation/admin.py Normal file
View File

@@ -0,0 +1,46 @@
from django.contrib import admin
from compensation.models import Compensation, CompensationAction, CompensationState, CompensationControl
class CompensationControlAdmin(admin.ModelAdmin):
list_display = [
"id",
"type",
"deadline",
"expected_result",
"by_authority",
]
class CompensationStateAdmin(admin.ModelAdmin):
list_display = [
"id",
"biotope_type",
"amount",
"unit",
]
class CompensationActionAdmin(admin.ModelAdmin):
list_display = [
"id",
"action_type",
"amount",
"unit",
"control",
]
class CompensationAdmin(admin.ModelAdmin):
list_display = [
"id",
"type",
"created_on",
]
admin.site.register(Compensation, CompensationAdmin)
admin.site.register(CompensationAction, CompensationActionAdmin)
admin.site.register(CompensationState, CompensationStateAdmin)
admin.site.register(CompensationControl, CompensationControlAdmin)

5
compensation/apps.py Normal file
View File

@@ -0,0 +1,5 @@
from django.apps import AppConfig
class CompensationConfig(AppConfig):
name = 'compensation'

14
compensation/forms.py Normal file
View File

@@ -0,0 +1,14 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 04.12.20
"""
from konova.forms import BaseForm
class NewCompensationForm(BaseForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

145
compensation/models.py Normal file
View File

@@ -0,0 +1,145 @@
"""
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
from django.contrib.gis.db import models
from django.utils.timezone import now
from compensation.settings import COMPENSATION_IDENTIFIER_LENGTH, COMPENSATION_IDENTIFIER_TEMPLATE
from konova.models import BaseObject, BaseResource
from konova.utils.generators import generate_random_string
from process.models import Process
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.CharField(max_length=500, null=True, blank=True)
comment = models.TextField()
class CompensationState(models.Model):
"""
Compensations must define the state of an area before and after the compensation.
"""
biotope_type = models.CharField(max_length=500, null=True, blank=True)
amount = models.FloatField()
unit = models.CharField(max_length=100, null=True, blank=True)
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)
control = models.ForeignKey(CompensationControl, on_delete=models.SET_NULL, null=True, blank=True)
class Compensation(BaseObject):
"""
The compensation holds information about which actions have to be performed until which date, who is in charge
of this, which legal authority is the point of contact, and so on.
"""
is_old_law = models.BooleanField(default=False)
type = models.CharField(max_length=500, null=True, blank=True)
registration_office = models.CharField(max_length=500, null=True, blank=True) # ToDo: Really needed?
process = models.ForeignKey("process.Process", related_name="compensations", on_delete=models.CASCADE)
ground_definitions = models.CharField(max_length=500, null=True, blank=True) # ToDo: Need to be M2M to laws!
action_definitions = models.CharField(max_length=500, null=True, blank=True) # ToDo: Need to be M2M to laws!
actions = models.ManyToManyField(CompensationAction)
deadline_creation = models.ForeignKey("konova.Deadline", on_delete=models.SET_NULL, null=True, blank=True, related_name="deadline_creation")
deadline_maintaining = models.ForeignKey("konova.Deadline", on_delete=models.SET_NULL, null=True, blank=True, related_name="deadline_maintaining")
initial_states = models.ManyToManyField(CompensationState, blank=True, related_name='+')
final_states = models.ManyToManyField(CompensationState, blank=True, related_name='+')
geometry = models.MultiPolygonField(null=True, blank=True)
documents = models.ManyToManyField("konova.Document", blank=True)
def __str__(self):
return "{} of {}".format(self.type, self.process)
@staticmethod
def __generate_new_identifier() -> str:
""" 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 save(self, *args, **kwargs):
if self.identifier is None or len(self.identifier) == 0:
# Create new identifier
new_id = self.__generate_new_identifier()
while Compensation.objects.filter(identifier=new_id).exists():
new_id = self.__generate_new_identifier()
self.identifier = new_id
super().save(*args, **kwargs)
@staticmethod
def get_role_objects(user: User, order_by: str = "-created_on"):
""" Returns objects depending on the currently selected role of the user
* REGISTRATIONOFFICE
* User can see the processes where registration_office is set to the organisation of the currently selected role
* User can see self-created processes
* LICENSINGOFFICE
* same
* DATAPROVIDER
* User can see only self-created processes
Args:
user (User): The performing user
order_by (str): Order by which Process attribute
Returns:
"""
role = user.current_role
if role is None:
return Compensation.objects.none()
processes = Process.get_role_objects(user, order_by)
processes.prefetch_related("compensations")
compensations = []
[compensations.extend(process.compensations.all()) for process in processes]
return compensations
class EcoAccount(BaseResource):
"""
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.
'Withdrawing' can only be applied by shrinking the size of the available geometry and declaring the withdrawed
geometry as a compensation for a process.
"""
is_old_law = models.BooleanField(default=False)
type = models.CharField(max_length=500, null=True, blank=True)
licensing_authority_document_identifier = models.CharField(max_length=500, null=True, blank=True)
registration_office = models.CharField(max_length=500, null=True, blank=True)
handler = models.CharField(max_length=500, null=True, blank=True)
handler_comments = models.TextField()
geometry = models.GeometryCollectionField()
documents = models.ManyToManyField("konova.Document")
initial_states = models.ManyToManyField(CompensationState, blank=True, related_name='+')
final_states = models.ManyToManyField(CompensationState, blank=True, related_name='+')
actions = models.ManyToManyField(CompensationAction)
deadline_maintaining = models.ForeignKey("konova.Deadline", on_delete=models.SET_NULL, null=True, blank=True)
deadline_other = models.ManyToManyField("konova.Deadline", blank=True, related_name='+')
comments = models.TextField()

9
compensation/settings.py Normal file
View File

@@ -0,0 +1,9 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 18.12.20
"""
COMPENSATION_IDENTIFIER_LENGTH = 10
COMPENSATION_IDENTIFIER_TEMPLATE = "KOM-{}"

118
compensation/tables.py Normal file
View File

@@ -0,0 +1,118 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 01.12.20
"""
from django.urls import reverse
from django.utils.html import format_html
from django.utils.translation import gettext_lazy as _
from konova.utils.tables import BaseTable
import django_tables2 as tables
class CompensationTable(BaseTable):
id = tables.Column(
verbose_name=_("Identifier"),
orderable=True,
accessor="identifier",
)
t = tables.Column(
verbose_name=_("Title"),
orderable=True,
accessor="title",
)
p = tables.Column(
verbose_name=_("Process"),
orderable=True,
accessor="process",
)
d = tables.Column(
verbose_name=_("Created on"),
orderable=True,
accessor="created_on",
)
ac = tables.Column(
verbose_name=_("Actions"),
orderable=False,
empty_values=[],
attrs={"td": {"class": "action-col"}}
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.title = _("Compensations")
self.add_new_url = reverse("compensation:new")
def render_ac(self, value, record):
"""
Renders possible actions for this record, such as delete.
"""
intervention = _("Compensation")
html = ""
html += self.render_open_btn(
_("Open {}").format(intervention),
reverse("compensation:open", args=(record.id,)),
new_tab=True
)
html += self.render_edit_btn(
_("Edit {}").format(intervention),
reverse("compensation:edit", args=(record.id,)),
)
html += self.render_delete_btn(
_("Delete {}").format(intervention),
reverse("compensation:remove", args=(record.id,)),
)
return format_html(html)
class EcoAccountTable(BaseTable):
id = tables.Column(
verbose_name=_("Identifier"),
orderable=True,
accessor="identifier",
)
t = tables.Column(
verbose_name=_("Title"),
orderable=True,
accessor="title",
)
d = tables.Column(
verbose_name=_("Created on"),
orderable=True,
accessor="created_on",
)
ac = tables.Column(
verbose_name=_("Actions"),
orderable=False,
empty_values=[],
attrs={"td": {"class": "action-col"}}
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.title = _("Eco Accounts")
self.add_new_url = reverse("compensation:account-new")
def render_ac(self, value, record):
"""
Renders possible actions for this record, such as delete.
"""
intervention = _("Compensation")
html = ""
html += self.render_open_btn(
_("Open {}").format(intervention),
reverse("compensation:open", args=(record.id,)),
new_tab=True
)
html += self.render_edit_btn(
_("Edit {}").format(intervention),
reverse("compensation:edit", args=(record.id,)),
)
html += self.render_delete_btn(
_("Delete {}").format(intervention),
reverse("compensation:remove", args=(record.id,)),
)
return format_html(html)

3
compensation/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

27
compensation/urls.py Normal file
View File

@@ -0,0 +1,27 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 30.11.20
"""
from django.urls import path
from compensation.views import *
app_name = "compensation"
urlpatterns = [
# Main compensation
path("", index_view, name="index"),
path('new/', new_view, name='new'),
path('open/<id>', open_view, name='open'),
path('edit/<id>', edit_view, name='edit'),
path('remove/<id>', remove_view, name='remove'),
# Eco-account
path("account/", account_index_view, name="account-index"),
path('account/new/', account_new_view, name='account-new'),
path('account/open/<id>', account_open_view, name='account-open'),
path('account/edit/<id>', account_edit_view, name='account-edit'),
path('account/remove/<id>', account_remove_view, name='account-remove'),
]

110
compensation/views.py Normal file
View File

@@ -0,0 +1,110 @@
from django.contrib.auth.decorators import login_required
from django.http import HttpRequest
from django.shortcuts import render
from compensation.models import Compensation, EcoAccount
from compensation.tables import CompensationTable, EcoAccountTable
from konova.contexts import BaseContext
from konova.decorators import *
@login_required
@resolve_user_role
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"
user = request.user
compensations = Compensation.get_role_objects(user)
table = CompensationTable(
request=request,
queryset=compensations
)
context = {
"table": table,
}
context = BaseContext(request, context).context
return render(request, template, context)
@login_required
def new_view(request: HttpRequest):
# ToDo
pass
@login_required
def edit_view(request: HttpRequest, id: str):
# ToDo
pass
@login_required
def open_view(request: HttpRequest, id: str):
# ToDo
pass
@login_required
def remove_view(request: HttpRequest, id: str):
# ToDo
pass
@login_required
def account_index_view(request: HttpRequest):
"""
Renders the index view for eco accounts
Args:
request (HttpRequest): The incoming request
Returns:
A rendered view
"""
template = "generic_index.html"
user = request.user
eco_accounts = EcoAccount.objects.filter(
created_by=user,
is_deleted=False,
)
table = EcoAccountTable(
request=request,
queryset=eco_accounts
)
context = {
"table": table,
}
context = BaseContext(request, context).context
return render(request, template, context)
@login_required
def account_new_view(request: HttpRequest):
# ToDo
pass
@login_required
def account_edit_view(request: HttpRequest, id: str):
# ToDo
pass
@login_required
def account_open_view(request: HttpRequest, id: str):
# ToDo
pass
@login_required
def account_remove_view(request: HttpRequest, id: str):
# ToDo
pass