EMA
* adds Ema model (basically Compensation inherited) * adds index view for EMAs * fixes drop-down link bug for menu 'More' in navbar * refactors some more forms to use process_request() * adds modified attribute to BaseResource for easy last_modified check * adds setting of modified attribute in all places where UserAction.EDITED is added to log * adds EMA_ACCOUNT_IDENTIFIER_LENGTH and EMA_ACCOUNT_IDENTIFIER_TEMPLATE to ema/settings.py * adds EmaAdmin to ema/admin.py * fixes wrong title in intervention detail view html for revocations * adds support for subtitle variable to BaseTable and generic_index.html * drops next_version attribute from models * adds/updates translations * adds default ordering for UserActionLogEntry * removes extra ordering in log modal rendering
This commit is contained in:
0
ema/__init__.py
Normal file
0
ema/__init__.py
Normal file
10
ema/admin.py
Normal file
10
ema/admin.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from compensation.admin import CompensationAdmin
|
||||
from ema.models import Ema
|
||||
|
||||
|
||||
class EmaAdmin(CompensationAdmin):
|
||||
pass
|
||||
|
||||
admin.site.register(Ema, EmaAdmin)
|
||||
5
ema/apps.py
Normal file
5
ema/apps.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class EmaConfig(AppConfig):
|
||||
name = 'ema'
|
||||
53
ema/filters.py
Normal file
53
ema/filters.py
Normal file
@@ -0,0 +1,53 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||
Created on: 19.08.21
|
||||
|
||||
"""
|
||||
from django.db.models import QuerySet
|
||||
|
||||
from compensation.filters import CompensationTableFilter
|
||||
|
||||
|
||||
class EmaTableFilter(CompensationTableFilter):
|
||||
"""
|
||||
Since EMA and compensation are basically the same, we can reuse CompensationTableFilter and extend the MAE filter
|
||||
in the future by inheriting.
|
||||
"""
|
||||
|
||||
def _filter_show_all(self, queryset, name, value) -> QuerySet:
|
||||
""" Filters queryset depending on value of 'show_all' setting
|
||||
|
||||
Args:
|
||||
queryset ():
|
||||
name ():
|
||||
value ():
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
if not value:
|
||||
return queryset.filter(
|
||||
users__in=[self.user], # requesting user has access
|
||||
)
|
||||
else:
|
||||
return queryset
|
||||
|
||||
def _filter_show_recorded(self, queryset, name, value) -> QuerySet:
|
||||
""" Filters queryset depending on value of 'show_recorded' setting
|
||||
|
||||
Args:
|
||||
queryset ():
|
||||
name ():
|
||||
value ():
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
if not value:
|
||||
return queryset.filter(
|
||||
recorded=None,
|
||||
)
|
||||
else:
|
||||
return queryset
|
||||
74
ema/models.py
Normal file
74
ema/models.py
Normal file
@@ -0,0 +1,74 @@
|
||||
from django.contrib.auth.models import User
|
||||
from django.db import models
|
||||
|
||||
from compensation.models import AbstractCompensation
|
||||
from konova.settings import DEFAULT_SRID_RLP, LANIS_LINK_TEMPLATE
|
||||
from user.models import UserActionLogEntry
|
||||
|
||||
|
||||
class Ema(AbstractCompensation):
|
||||
"""
|
||||
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, ...
|
||||
|
||||
"""
|
||||
# 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 (shared with)"
|
||||
)
|
||||
|
||||
# Refers to "verzeichnen"
|
||||
recorded = models.OneToOneField(
|
||||
UserActionLogEntry,
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Holds data on user and timestamp of this action",
|
||||
related_name="+"
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return "{}".format(self.identifier)
|
||||
|
||||
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 get_LANIS_link(self) -> str:
|
||||
""" Generates a link for LANIS depending on the geometry
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
try:
|
||||
geom = self.geometry.geom.transform(DEFAULT_SRID_RLP, clone=True)
|
||||
x = geom.centroid.x
|
||||
y = geom.centroid.y
|
||||
zoom_lvl = 16
|
||||
except AttributeError:
|
||||
# If no geometry has been added, yet.
|
||||
x = 1
|
||||
y = 1
|
||||
zoom_lvl = 6
|
||||
return LANIS_LINK_TEMPLATE.format(
|
||||
zoom_lvl,
|
||||
x,
|
||||
y,
|
||||
)
|
||||
10
ema/settings.py
Normal file
10
ema/settings.py
Normal file
@@ -0,0 +1,10 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||
Created on: 19.08.21
|
||||
|
||||
"""
|
||||
|
||||
EMA_ACCOUNT_IDENTIFIER_LENGTH = 10
|
||||
EMA_ACCOUNT_IDENTIFIER_TEMPLATE = "EMA-{}"
|
||||
132
ema/tables.py
Normal file
132
ema/tables.py
Normal file
@@ -0,0 +1,132 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||
Created on: 19.08.21
|
||||
|
||||
"""
|
||||
from django.http import HttpRequest
|
||||
from django.utils.html import format_html
|
||||
from django.utils.timezone import localtime
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.urls import reverse
|
||||
|
||||
import django_tables2 as tables
|
||||
|
||||
from konova.sub_settings.django_settings import DEFAULT_DATE_TIME_FORMAT
|
||||
from konova.utils.tables import BaseTable
|
||||
from ema.filters import EmaTableFilter
|
||||
from ema.models import Ema
|
||||
|
||||
|
||||
class EmaTable(BaseTable):
|
||||
"""
|
||||
Since EMA and compensation are basically the same, we can reuse CompensationTableFilter and extend the EMA filter
|
||||
in the future by inheriting.
|
||||
"""
|
||||
id = tables.Column(
|
||||
verbose_name=_("Identifier"),
|
||||
orderable=True,
|
||||
accessor="identifier",
|
||||
)
|
||||
t = tables.Column(
|
||||
verbose_name=_("Title"),
|
||||
orderable=True,
|
||||
accessor="title",
|
||||
)
|
||||
r = tables.Column(
|
||||
verbose_name=_("Recorded"),
|
||||
orderable=True,
|
||||
empty_values=[],
|
||||
accessor="recorded",
|
||||
)
|
||||
e = tables.Column(
|
||||
verbose_name=_("Editable"),
|
||||
orderable=True,
|
||||
empty_values=[],
|
||||
accessor="users",
|
||||
)
|
||||
lm = tables.Column(
|
||||
verbose_name=_("Last edit"),
|
||||
orderable=True,
|
||||
accessor="created__timestamp",
|
||||
)
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
template_name = "django_tables2/bootstrap4.html"
|
||||
|
||||
def __init__(self, request: HttpRequest, *args, **kwargs):
|
||||
self.title = _("Payment funded compensations")
|
||||
self.subtitle = _("EMA explanation")
|
||||
self.add_new_url = reverse("ema:new")
|
||||
qs = kwargs.get("queryset", None)
|
||||
self.filter = EmaTableFilter(
|
||||
user=request.user,
|
||||
data=request.GET,
|
||||
queryset=qs,
|
||||
)
|
||||
super().__init__(request, self.filter, *args, **kwargs)
|
||||
|
||||
def render_id(self, value, record: Ema):
|
||||
""" Renders the id column for a EMA
|
||||
|
||||
Args:
|
||||
value (str): The identifier value
|
||||
record (EMA): The EMA record
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
html = ""
|
||||
html += self.render_link(
|
||||
tooltip=_("Open {}").format(_("EMA")),
|
||||
href=reverse("ema:open", args=(record.id,)),
|
||||
txt=value,
|
||||
new_tab=False,
|
||||
)
|
||||
return format_html(html)
|
||||
|
||||
def render_r(self, value, record: Ema):
|
||||
""" Renders the registered column for a EMA
|
||||
|
||||
Args:
|
||||
value (str): The identifier value
|
||||
record (Ema): The EMA record
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
html = ""
|
||||
recorded = value is not None
|
||||
tooltip = _("Not recorded yet")
|
||||
if recorded:
|
||||
value = value.timestamp
|
||||
value = localtime(value)
|
||||
on = value.strftime(DEFAULT_DATE_TIME_FORMAT)
|
||||
tooltip = _("Recorded on {} by {}").format(on, record.recorded.user)
|
||||
html += self.render_bookmark(
|
||||
tooltip=tooltip,
|
||||
icn_filled=recorded,
|
||||
)
|
||||
return format_html(html)
|
||||
|
||||
def render_e(self, value, record: Ema):
|
||||
""" Renders the editable column for a EMA
|
||||
|
||||
Args:
|
||||
value (str): The identifier value
|
||||
record (Ema): The EMA record
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
html = ""
|
||||
has_access = value.filter(
|
||||
username=self.user.username
|
||||
).exists()
|
||||
|
||||
html += self.render_icn(
|
||||
tooltip=_("Full access granted") if has_access else _("Access not granted"),
|
||||
icn_class="fas fa-edit rlp-r-inv" if has_access else "far fa-edit",
|
||||
)
|
||||
return format_html(html)
|
||||
3
ema/tests.py
Normal file
3
ema/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
16
ema/urls.py
Normal file
16
ema/urls.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||
Created on: 19.08.21
|
||||
|
||||
"""
|
||||
from django.urls import path
|
||||
from ema.views import index_view, new_view, open_view
|
||||
|
||||
app_name = "ema"
|
||||
urlpatterns = [
|
||||
path("", index_view, name="index"),
|
||||
path("new/", new_view, name="new"),
|
||||
path("<id>", open_view, name="open"),
|
||||
]
|
||||
70
ema/views.py
Normal file
70
ema/views.py
Normal file
@@ -0,0 +1,70 @@
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.http import HttpRequest
|
||||
from django.shortcuts import render, get_object_or_404
|
||||
|
||||
from ema.tables import EmaTable
|
||||
from konova.contexts import BaseContext
|
||||
from konova.decorators import conservation_office_group_required
|
||||
from ema.models import Ema
|
||||
|
||||
|
||||
@login_required
|
||||
def index_view(request: HttpRequest):
|
||||
""" Renders the index view for MAEs
|
||||
|
||||
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 the form for a new MAE
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
template = "generic_index.html"
|
||||
context = {}
|
||||
context = BaseContext(request, context).context
|
||||
return render(request, template, context)
|
||||
|
||||
|
||||
@login_required
|
||||
def open_view(request: HttpRequest, id: str):
|
||||
""" Renders the form for a new MAE
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
id (str): The MAE id
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
ema = get_object_or_404(Ema, id=id)
|
||||
template = "generic_index.html"
|
||||
context = {}
|
||||
context = BaseContext(request, context).context
|
||||
return render(request, template, context)
|
||||
Reference in New Issue
Block a user