Merge pull request '25_Public_Reports' (#30) from 25_Public_Reports into master

Reviewed-on: SGD-Nord/konova#30
This commit is contained in:
Michel Peltriaux 2021-10-15 09:27:05 +02:00
commit e170f283ce
61 changed files with 1270 additions and 370 deletions

View File

@ -12,9 +12,10 @@ urlpatterns = [
path("", index_view, name="acc-index"), path("", index_view, name="acc-index"),
path('new/', new_view, name='acc-new'), path('new/', new_view, name='acc-new'),
path('new/id', new_id_view, name='acc-new-id'), path('new/id', new_id_view, name='acc-new-id'),
path('<id>', open_view, name='acc-open'), path('<id>', detail_view, name='acc-detail'),
path('<id>/log', log_view, name='acc-log'), path('<id>/log', log_view, name='acc-log'),
path('<id>/record', record_view, name='acc-record'), path('<id>/record', record_view, name='acc-record'),
path('<id>/report', report_view, name='acc-report'),
path('<id>/edit', edit_view, name='acc-edit'), path('<id>/edit', edit_view, name='acc-edit'),
path('<id>/remove', remove_view, name='acc-remove'), path('<id>/remove', remove_view, name='acc-remove'),
path('<id>/state/new', state_new_view, name='acc-new-state'), path('<id>/state/new', state_new_view, name='acc-new-state'),

View File

@ -14,13 +14,14 @@ urlpatterns = [
path('new/id', new_id_view, name='new-id'), path('new/id', new_id_view, name='new-id'),
path('new/<intervention_id>', new_view, name='new'), path('new/<intervention_id>', new_view, name='new'),
path('new', new_view, name='new'), path('new', new_view, name='new'),
path('<id>', open_view, name='open'), path('<id>', detail_view, name='detail'),
path('<id>/log', log_view, name='log'), path('<id>/log', log_view, name='log'),
path('<id>/edit', edit_view, name='edit'), path('<id>/edit', edit_view, name='edit'),
path('<id>/remove', remove_view, name='remove'), path('<id>/remove', remove_view, name='remove'),
path('<id>/state/new', state_new_view, name='new-state'), path('<id>/state/new', state_new_view, name='new-state'),
path('<id>/action/new', action_new_view, name='new-action'), path('<id>/action/new', action_new_view, name='new-action'),
path('<id>/deadline/new', deadline_new_view, name="new-deadline"), path('<id>/deadline/new', deadline_new_view, name="new-deadline"),
path('<id>/report', report_view, name='report'),
# Documents # Documents
path('<id>/document/new/', new_document_view, name='new-doc'), path('<id>/document/new/', new_document_view, name='new-doc'),

View File

@ -179,7 +179,7 @@ class NewCompensationForm(AbstractCompensationForm):
self.initialize_form_field("intervention", intervention_id) self.initialize_form_field("intervention", intervention_id)
self.disable_form_field("intervention") self.disable_form_field("intervention")
self.action_url = reverse("compensation:new", args=(intervention_id,)) self.action_url = reverse("compensation:new", args=(intervention_id,))
self.cancel_redirect = reverse("intervention:open", args=(intervention_id,)) self.cancel_redirect = reverse("intervention:detail", args=(intervention_id,))
else: else:
self.action_url = reverse("compensation:new") self.action_url = reverse("compensation:new")
self.cancel_redirect = reverse("compensation:index") self.cancel_redirect = reverse("compensation:index")
@ -230,7 +230,7 @@ class EditCompensationForm(NewCompensationForm):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.form_title = _("Edit compensation") self.form_title = _("Edit compensation")
self.action_url = reverse("compensation:edit", args=(self.instance.id,)) self.action_url = reverse("compensation:edit", args=(self.instance.id,))
self.cancel_redirect = reverse("compensation:open", args=(self.instance.id,)) self.cancel_redirect = reverse("compensation:detail", args=(self.instance.id,))
# Initialize form data # Initialize form data
form_data = { form_data = {
@ -384,7 +384,7 @@ class EditEcoAccountForm(NewEcoAccountForm):
self.form_title = _("Edit Eco-Account") self.form_title = _("Edit Eco-Account")
self.action_url = reverse("compensation:acc-edit", args=(self.instance.id,)) self.action_url = reverse("compensation:acc-edit", args=(self.instance.id,))
self.cancel_redirect = reverse("compensation:acc-open", args=(self.instance.id,)) self.cancel_redirect = reverse("compensation:acc-detail", args=(self.instance.id,))
# Initialize form data # Initialize form data
form_data = { form_data = {

73
compensation/managers.py Normal file
View File

@ -0,0 +1,73 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 14.10.21
"""
from django.db import models
class CompensationActionManager(models.Manager):
""" Holds default db fetch setting for this model type
"""
def get_queryset(self):
return super().get_queryset().select_related(
"action_type",
"action_type__parent"
)
class CompensationStateManager(models.Manager):
""" Holds default db fetch setting for this model type
"""
def get_queryset(self):
return super().get_queryset().select_related(
"biotope_type",
"biotope_type__parent"
)
class CompensationManager(models.Manager):
""" Holds default db fetch setting for this model type
"""
def get_queryset(self):
return super().get_queryset().select_related(
"modified",
"intervention",
"intervention__recorded",
"intervention__recorded__user",
"intervention__modified",
"intervention__checked",
"intervention__checked__user",
)
class EcoAccountManager(models.Manager):
""" Holds default db fetch setting for this model type
"""
def get_queryset(self):
return super().get_queryset().select_related(
"recorded",
"recorded__user",
"modified",
"modified__user",
).prefetch_related(
"users",
)
class EcoAccountDeductionManager(models.Manager):
""" Holds default db fetch setting for this model type
"""
def get_queryset(self):
return super().get_queryset().select_related(
"intervention",
"intervention__recorded",
"created",
)

View File

@ -17,6 +17,8 @@ from django.utils.translation import gettext_lazy as _
from codelist.models import KonovaCode from codelist.models import KonovaCode
from codelist.settings import CODELIST_COMPENSATION_ACTION_ID, CODELIST_BIOTOPES_ID, \ from codelist.settings import CODELIST_COMPENSATION_ACTION_ID, CODELIST_BIOTOPES_ID, \
CODELIST_COMPENSATION_FUNDING_ID CODELIST_COMPENSATION_FUNDING_ID
from compensation.managers import CompensationStateManager, EcoAccountDeductionManager, CompensationActionManager, \
EcoAccountManager, CompensationManager
from intervention.models import Intervention, ResponsibilityData from intervention.models import Intervention, ResponsibilityData
from konova.models import BaseObject, BaseResource, Geometry, UuidModel, AbstractDocument, \ from konova.models import BaseObject, BaseResource, Geometry, UuidModel, AbstractDocument, \
generate_document_file_upload_path generate_document_file_upload_path
@ -67,6 +69,8 @@ class CompensationState(UuidModel):
) )
surface = models.FloatField() surface = models.FloatField()
objects = CompensationStateManager()
def __str__(self): def __str__(self):
return "{} | {}".format(self.biotope_type, self.surface) return "{} | {}".format(self.biotope_type, self.surface)
@ -102,6 +106,8 @@ class CompensationAction(BaseResource):
unit = models.CharField(max_length=100, null=True, blank=True, choices=UnitChoices.choices) unit = models.CharField(max_length=100, null=True, blank=True, choices=UnitChoices.choices)
comment = models.TextField(blank=True, null=True, help_text="Additional comment") comment = models.TextField(blank=True, null=True, help_text="Additional comment")
objects = CompensationActionManager()
def __str__(self): def __str__(self):
return "{} | {} {}".format(self.action_type, self.amount, self.unit) return "{} | {} {}".format(self.action_type, self.amount, self.unit)
@ -178,6 +184,8 @@ class Compensation(AbstractCompensation):
related_name='compensations' related_name='compensations'
) )
objects = CompensationManager()
def __str__(self): def __str__(self):
return "{}".format(self.identifier) return "{}".format(self.identifier)
@ -301,6 +309,8 @@ class EcoAccount(AbstractCompensation):
default=0, default=0,
) )
objects = EcoAccountManager()
def __str__(self): def __str__(self):
return "{}".format(self.identifier) return "{}".format(self.identifier)
@ -500,5 +510,7 @@ class EcoAccountDeduction(BaseResource):
related_name="deductions", related_name="deductions",
) )
objects = EcoAccountDeductionManager()
def __str__(self): def __str__(self):
return "{} of {}".format(self.surface, self.account) return "{} of {}".format(self.surface, self.account)

View File

@ -82,7 +82,7 @@ class CompensationTable(BaseTable):
html = "" html = ""
html += self.render_link( html += self.render_link(
tooltip=_("Open {}").format(_("Compensation")), tooltip=_("Open {}").format(_("Compensation")),
href=reverse("compensation:open", args=(record.id,)), href=reverse("compensation:detail", args=(record.id,)),
txt=value, txt=value,
new_tab=False, new_tab=False,
) )
@ -148,14 +148,13 @@ class CompensationTable(BaseTable):
Returns: Returns:
""" """
html = ""
if value is None: if value is None:
value = User.objects.none() value = User.objects.none()
has_access = value.filter( has_access = value.filter(
username=self.user.username id=self.user.id
).exists() ).exists()
html += self.render_icn( html = self.render_icn(
tooltip=_("Full access granted") if has_access else _("Access not granted"), 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", icn_class="fas fa-edit rlp-r-inv" if has_access else "far fa-edit",
) )
@ -223,7 +222,7 @@ class EcoAccountTable(BaseTable):
html = "" html = ""
html += self.render_link( html += self.render_link(
tooltip=_("Open {}").format(_("Eco-account")), tooltip=_("Open {}").format(_("Eco-account")),
href=reverse("compensation:acc-open", args=(record.id,)), href=reverse("compensation:acc-detail", args=(record.id,)),
txt=value, txt=value,
new_tab=False, new_tab=False,
) )
@ -244,7 +243,7 @@ class EcoAccountTable(BaseTable):
return format_html(html) return format_html(html)
def render_r(self, value, record: EcoAccount): def render_r(self, value, record: EcoAccount):
""" Renders the registered column for an eco account """ Renders the recorded column for an eco account
Args: Args:
value (str): The identifier value value (str): The identifier value
@ -268,7 +267,7 @@ class EcoAccountTable(BaseTable):
return format_html(html) return format_html(html)
def render_e(self, value, record: EcoAccount): def render_e(self, value, record: EcoAccount):
""" Renders the registered column for an eco account """ Renders the editable column for an eco account
Args: Args:
value (str): The identifier value value (str): The identifier value
@ -278,10 +277,9 @@ class EcoAccountTable(BaseTable):
""" """
html = "" html = ""
has_access = value.filter( # Do not use value in here, since value does use unprefetched 'users' manager, where record has already
username=self.user.username # prefetched users data
).exists() has_access = self.user in record.users.all()
html += self.render_icn( html += self.render_icn(
tooltip=_("Full access granted") if has_access else _("Access not granted"), 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", icn_class="fas fa-edit rlp-r-inv" if has_access else "far fa-edit",

View File

@ -4,7 +4,7 @@
<div class="row"> <div class="row">
<div class="col-sm-6"> <div class="col-sm-6">
<h5> <h5>
<span class="badge badge-light">{{obj.actions.count}}</span> <span class="badge badge-light">{{actions.count}}</span>
{% trans 'Actions' context 'Compensation' %} {% trans 'Actions' context 'Compensation' %}
</h5> </h5>
</div> </div>
@ -33,13 +33,15 @@
<th scope="col"> <th scope="col">
{% trans 'Comment' %} {% trans 'Comment' %}
</th> </th>
{% if is_default_member and has_access %}
<th scope="col"> <th scope="col">
{% trans 'Action' %} {% trans 'Action' %}
</th> </th>
{% endif %}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for action in obj.actions.all %} {% for action in actions %}
<tr> <tr>
<td class="align-middle"> <td class="align-middle">
{{ action.action_type }} {{ action.action_type }}

View File

@ -6,7 +6,7 @@
LANIS LANIS
</button> </button>
</a> </a>
<a href="{% url 'home' %}" class="mr-2"> <a href="{% url 'compensation:report' obj.id %}" target="_blank" class="mr-2">
<button class="btn btn-default" title="{% trans 'Public report' %}"> <button class="btn btn-default" title="{% trans 'Public report' %}">
{% fa5_icon 'file-alt' %} {% fa5_icon 'file-alt' %}
</button> </button>

View File

@ -33,9 +33,11 @@
<th scope="col"> <th scope="col">
{% trans 'Comment' %} {% trans 'Comment' %}
</th> </th>
{% if is_default_member and has_access %}
<th scope="col"> <th scope="col">
{% trans 'Action' %} {% trans 'Action' %}
</th> </th>
{% endif %}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>

View File

@ -30,9 +30,11 @@
<th scope="col"> <th scope="col">
{% trans 'Comment' %} {% trans 'Comment' %}
</th> </th>
{% if is_default_member and has_access %}
<th scope="col"> <th scope="col">
{% trans 'Action' %} {% trans 'Action' %}
</th> </th>
{% endif %}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>

View File

@ -35,9 +35,11 @@
<th scope="col"> <th scope="col">
{% trans 'Surface' %} {% trans 'Surface' %}
</th> </th>
{% if is_default_member and has_access %}
<th scope="col"> <th scope="col">
{% trans 'Action' %} {% trans 'Action' %}
</th> </th>
{% endif %}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>

View File

@ -4,7 +4,7 @@
<div class="row"> <div class="row">
<div class="col-sm-6"> <div class="col-sm-6">
<h5> <h5>
<span class="badge badge-light">{{obj.before_states.count}}</span> <span class="badge badge-light">{{before_states.count}}</span>
{% trans 'States before' %} {% trans 'States before' %}
</h5> </h5>
</div> </div>
@ -35,9 +35,11 @@
<th scope="col"> <th scope="col">
{% trans 'Surface' %} {% trans 'Surface' %}
</th> </th>
{% if is_default_member and has_access %}
<th scope="col"> <th scope="col">
{% trans 'Action' %} {% trans 'Action' %}
</th> </th>
{% endif %}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>

View File

@ -33,7 +33,7 @@
<tr> <tr>
<th scope="row">{% trans 'compensates intervention' %}</th> <th scope="row">{% trans 'compensates intervention' %}</th>
<td class="align-middle"> <td class="align-middle">
<a href="{% url 'intervention:open' obj.intervention.id %}"> <a href="{% url 'intervention:detail' obj.intervention.id %}">
{{obj.intervention.identifier}} {{obj.intervention.identifier}}
</a> </a>
</td> </td>
@ -103,7 +103,7 @@
{% include 'map/geom_form.html' %} {% include 'map/geom_form.html' %}
</div> </div>
<div class="row"> <div class="row">
{% include 'compensation/detail/compensation/includes/comment.html' %} {% include 'konova/comment_card.html' %}
</div> </div>
</div> </div>
</div> </div>

View File

@ -4,7 +4,7 @@
<div class="row"> <div class="row">
<div class="col-sm-6"> <div class="col-sm-6">
<h5> <h5>
<span class="badge badge-light">{{obj.actions.count}}</span> <span class="badge badge-light">{{actions.count}}</span>
{% trans 'Actions' context 'Compensation' %} {% trans 'Actions' context 'Compensation' %}
</h5> </h5>
</div> </div>
@ -33,13 +33,15 @@
<th scope="col"> <th scope="col">
{% trans 'Comment' %} {% trans 'Comment' %}
</th> </th>
{% if default_member and has_access %}
<th scope="col"> <th scope="col">
{% trans 'Action' %} {% trans 'Action' %}
</th> </th>
{% endif %}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for action in obj.actions.all %} {% for action in actions %}
<tr> <tr>
<td class="align-middle"> <td class="align-middle">
{{ action.action_type }} {{ action.action_type }}

View File

@ -1,23 +0,0 @@
{% load i18n fontawesome_5 %}
{% if obj.comment %}
<div class="w-100">
<div class="card mt-3">
<div class="card-header rlp-gd">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-12">
<h5 class="card-title">
{% fa5_icon 'info-circle' %}
{% trans 'Comment' %}
</h5>
</div>
</div>
</div>
<div class="card-body">
<div class="card-text font-italic">
{{obj.comment}}
</div>
</div>
</div>
</div>
{% endif %}

View File

@ -6,7 +6,7 @@
LANIS LANIS
</button> </button>
</a> </a>
<a href="{% url 'home' %}" class="mr-2"> <a href="{% url 'compensation:acc-report' obj.id %}" target="_blank" class="mr-2">
<button class="btn btn-default" title="{% trans 'Public report' %}"> <button class="btn btn-default" title="{% trans 'Public report' %}">
{% fa5_icon 'file-alt' %} {% fa5_icon 'file-alt' %}
</button> </button>

View File

@ -45,15 +45,15 @@
{% for deduction in deductions %} {% for deduction in deductions %}
<tr> <tr>
<td class="align-middle"> <td class="align-middle">
<a href="{% url 'intervention:open' deduction.intervention.id %}"> <a href="{% url 'intervention:detail' deduction.intervention.id %}">
{{ deduction.intervention.identifier }} {{ deduction.intervention.identifier }}
</a> </a>
</td> </td>
<td class="align-middle"> <td class="align-middle">
{% if deduction.intervention.recorded %} {% if deduction.intervention.recorded %}
<em title='{{ deduction.intervention.recorded_tooltip }}' class='fas fa-bookmark registered-bookmark'></em> <em title="{% trans 'Recorded on' %} {{obj.recorded.timestamp}} {% trans 'by' %} {{obj.recorded.user}}" class='fas fa-bookmark registered-bookmark'></em>
{% else %} {% else %}
<em title='{{ deduction.intervention.recorded_tooltip }}' class='far fa-bookmark'></em> <em title="{% trans 'Not recorded yet' %}" class='far fa-bookmark'></em>
{% endif %} {% endif %}
</td> </td>
<td class="align-middle">{{ deduction.surface|floatformat:2|intcomma }} m²</td> <td class="align-middle">{{ deduction.surface|floatformat:2|intcomma }} m²</td>

View File

@ -4,7 +4,7 @@
<div class="row"> <div class="row">
<div class="col-sm-6"> <div class="col-sm-6">
<h5> <h5>
<span class="badge badge-light">{{obj.after_states.count}}</span> <span class="badge badge-light">{{after_states.count}}</span>
{% trans 'States after' %} {% trans 'States after' %}
</h5> </h5>
</div> </div>
@ -35,9 +35,11 @@
<th scope="col"> <th scope="col">
{% trans 'Surface' %} {% trans 'Surface' %}
</th> </th>
{% if is_default_member and has_access %}
<th scope="col"> <th scope="col">
{% trans 'Action' %} {% trans 'Action' %}
</th> </th>
{% endif %}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>

View File

@ -4,7 +4,7 @@
<div class="row"> <div class="row">
<div class="col-sm-6"> <div class="col-sm-6">
<h5> <h5>
<span class="badge badge-light">{{obj.before_states.count}}</span> <span class="badge badge-light">{{before_states.count}}</span>
{% trans 'States before' %} {% trans 'States before' %}
</h5> </h5>
</div> </div>
@ -35,9 +35,11 @@
<th scope="col"> <th scope="col">
{% trans 'Surface' %} {% trans 'Surface' %}
</th> </th>
{% if is_default_member and has_access %}
<th scope="col"> <th scope="col">
{% trans 'Action' %} {% trans 'Action' %}
</th> </th>
{% endif %}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>

View File

@ -62,7 +62,7 @@
<td class="align-middle">{{obj.responsible.conservation_file_number|default_if_none:""}}</td> <td class="align-middle">{{obj.responsible.conservation_file_number|default_if_none:""}}</td>
</tr> </tr>
<tr {% if not obj.responsible.handler %}class="alert alert-danger" title="{% trans 'Missing' %}" {% endif %}> <tr {% if not obj.responsible.handler %}class="alert alert-danger" title="{% trans 'Missing' %}" {% endif %}>
<th scope="row">{% trans 'Intervention handler' %}</th> <th scope="row">{% trans 'Action handler' %}</th>
<td class="align-middle">{{obj.responsible.handler|default_if_none:""}}</td> <td class="align-middle">{{obj.responsible.handler|default_if_none:""}}</td>
</tr> </tr>
<tr> <tr>
@ -102,7 +102,7 @@
{% include 'map/geom_form.html' %} {% include 'map/geom_form.html' %}
</div> </div>
<div class="row"> <div class="row">
{% include 'compensation/detail/compensation/includes/comment.html' %} {% include 'konova/comment_card.html' %}
</div> </div>
</div> </div>
</div> </div>

View File

@ -0,0 +1,67 @@
{% extends 'public_base.html' %}
{% load i18n fontawesome_5 humanize %}
{% block body %}
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<h3>{% trans 'Report' %}</h3>
<h4>{{obj.identifier}}</h4>
<div class="table-container">
<table class="table table-hover">
<tr>
<th class="w-25" scope="row">{% trans 'Title' %}</th>
<td class="align-middle">{{obj.title|default_if_none:""}}</td>
</tr>
<tr>
<th scope="row">{% trans 'compensates intervention' %}</th>
<td class="align-middle">
<a href="{% url 'intervention:report' obj.intervention.id %}">
{{obj.intervention.identifier}}
</a>
</td>
</tr>
<tr>
<th scope="row">{% trans 'Funded by' %}</th>
<td class="align-middle">
{% with obj.fundings.all as fundings %}
{% for funding in fundings %}
<div class="badge pill-badge rlp-r-outline">{{funding.short_name}}</div>
<br>
{% empty %}
{% trans 'None' %}
{% endfor %}
{% endwith %}
</td>
</tr>
<tr>
<th scope="row">{% trans 'Last modified' %}</th>
<td class="align-middle">
{{obj.modified.timestamp|default_if_none:""|naturalday}}
</td>
</tr>
</table>
</div>
{% include 'compensation/detail/compensation/includes/states-before.html' %}
{% include 'compensation/detail/compensation/includes/states-after.html' %}
{% include 'compensation/detail/compensation/includes/actions.html' %}
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="row">
{% include 'map/geom_form.html' %}
</div>
<div class="row">
<div class="col-sm-6 col-md-6 col-lg-6">
<h4>{% trans 'Open in browser' %}</h4>
{{ qrcode|safe }}
</div>
<div class="col-sm-6 col-md-6 col-lg-6">
<h4>{% trans 'View in LANIS' %}</h4>
{{ qrcode_lanis|safe }}
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,84 @@
{% extends 'public_base.html' %}
{% load i18n fontawesome_5 humanize %}
{% block body %}
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<h3>{% trans 'Report' %}</h3>
<h4>{{obj.identifier}}</h4>
<div class="table-container">
<table class="table table-hover">
<tr>
<th class="w-25" scope="row">{% trans 'Title' %}</th>
<td class="align-middle">{{obj.title|default_if_none:""}}</td>
</tr>
<tr>
<th scope="row">{% trans 'Conservation office' %}</th>
<td class="align-middle">{{obj.responsible.conservation_office.str_as_office|default_if_none:""}}</td>
</tr>
<tr>
<th scope="row">{% trans 'Conservation office file number' %}</th>
<td class="align-middle">{{obj.responsible.conservation_file_number|default_if_none:""}}</td>
</tr>
<tr>
<th scope="row">{% trans 'Action handler' %}</th>
<td class="align-middle">{{obj.responsible.handler|default_if_none:""}}</td>
</tr>
<tr>
<th scope="row">{% trans 'Funded by' %}</th>
<td class="align-middle">
{% with obj.fundings.all as fundings %}
{% for funding in fundings %}
<div class="badge pill-badge rlp-r-outline">{{funding.short_name}}</div>
<br>
{% empty %}
{% trans 'None' %}
{% endfor %}
{% endwith %}
</td>
</tr>
<tr>
<th scope="row">{% trans 'Deductions for' %}</th>
<td class="align-middle">
{% for deduction in deductions %}
<a href="{% url 'intervention:report' deduction.intervention__id %}">
{{deduction.intervention__identifier}}
</a>
<br>
{% empty %}
{% trans 'None' %}
{% endfor %}
</td>
</tr>
<tr>
<th scope="row">{% trans 'Last modified' %}</th>
<td class="align-middle">
{{obj.modified.timestamp|default_if_none:""|naturalday}}
</td>
</tr>
</table>
</div>
{% include 'compensation/detail/compensation/includes/states-before.html' %}
{% include 'compensation/detail/compensation/includes/states-after.html' %}
{% include 'compensation/detail/compensation/includes/actions.html' %}
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="row">
{% include 'map/geom_form.html' %}
</div>
<div class="row">
<div class="col-sm-6 col-md-6 col-lg-6">
<h4>{% trans 'Open in browser' %}</h4>
{{ qrcode|safe }}
</div>
<div class="col-sm-6 col-md-6 col-lg-6">
<h4>{% trans 'View in LANIS' %}</h4>
{{ qrcode_lanis|safe }}
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -13,6 +13,7 @@ from konova.contexts import BaseContext
from konova.decorators import * from konova.decorators import *
from konova.forms import RemoveModalForm, SimpleGeomForm, NewDocumentForm from konova.forms import RemoveModalForm, SimpleGeomForm, NewDocumentForm
from konova.utils.documents import get_document, remove_document from konova.utils.documents import get_document, remove_document
from konova.utils.generators import generate_qr_code
from konova.utils.message_templates import FORM_INVALID, IDENTIFIER_REPLACED from konova.utils.message_templates import FORM_INVALID, IDENTIFIER_REPLACED
from konova.utils.user_checks import in_group from konova.utils.user_checks import in_group
@ -74,7 +75,7 @@ def new_view(request: HttpRequest, intervention_id: str = None):
) )
) )
messages.success(request, _("Compensation {} added").format(comp.identifier)) messages.success(request, _("Compensation {} added").format(comp.identifier))
return redirect("compensation:open", id=comp.id) return redirect("compensation:detail", id=comp.id)
else: else:
messages.error(request, FORM_INVALID) messages.error(request, FORM_INVALID)
else: else:
@ -129,7 +130,7 @@ def edit_view(request: HttpRequest, id: str):
# The data form takes the geom form for processing, as well as the performing user # The data form takes the geom form for processing, as well as the performing user
comp = data_form.save(request.user, geom_form) comp = data_form.save(request.user, geom_form)
messages.success(request, _("Compensation {} edited").format(comp.identifier)) messages.success(request, _("Compensation {} edited").format(comp.identifier))
return redirect("compensation:open", id=comp.id) return redirect("compensation:detail", id=comp.id)
else: else:
messages.error(request, FORM_INVALID) messages.error(request, FORM_INVALID)
else: else:
@ -145,7 +146,7 @@ def edit_view(request: HttpRequest, id: str):
@login_required @login_required
@any_group_check @any_group_check
def open_view(request: HttpRequest, id: str): def detail_view(request: HttpRequest, id: str):
""" Renders a detail view for a compensation """ Renders a detail view for a compensation
Args: Args:
@ -162,8 +163,9 @@ def open_view(request: HttpRequest, id: str):
is_data_shared = comp.intervention.is_shared_with(_user) is_data_shared = comp.intervention.is_shared_with(_user)
# Order states according to surface # Order states according to surface
before_states = comp.before_states.all().order_by("-surface") before_states = comp.before_states.all().prefetch_related("biotope_type").order_by("-surface")
after_states = comp.after_states.all().order_by("-surface") after_states = comp.after_states.all().prefetch_related("biotope_type").order_by("-surface")
actions = comp.actions.all().prefetch_related("action_type")
# Precalculate logical errors between before- and after-states # Precalculate logical errors between before- and after-states
# Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling # Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling
@ -175,6 +177,7 @@ def open_view(request: HttpRequest, id: str):
"obj": comp, "obj": comp,
"geom_form": geom_form, "geom_form": geom_form,
"has_access": is_data_shared, "has_access": is_data_shared,
"actions": actions,
"before_states": before_states, "before_states": before_states,
"after_states": after_states, "after_states": after_states,
"sum_before_states": sum_before_states, "sum_before_states": sum_before_states,
@ -381,3 +384,53 @@ def action_remove_view(request: HttpRequest, id: str):
request, request,
msg_success=_("Action removed") msg_success=_("Action removed")
) )
def report_view(request:HttpRequest, id: str):
""" Renders the public report view
Args:
request (HttpRequest): The incoming request
id (str): The id of the intervention
Returns:
"""
# Reuse the compensation report template since compensations are structurally identical
template = "compensation/report/compensation/report.html"
comp = get_object_or_404(Compensation, id=id)
# If intervention is not recorded (yet or currently) we need to render another template without any data
if not comp.intervention.recorded:
template = "report/unavailable.html"
return render(request, template, {})
# Prepare data for map viewer
geom_form = SimpleGeomForm(
instance=comp
)
qrcode_img = generate_qr_code(
request.build_absolute_uri(reverse("compensation:report", args=(id,))),
10
)
qrcode_img_lanis = generate_qr_code(
comp.get_LANIS_link(),
7
)
# Order states by surface
before_states = comp.before_states.all().order_by("-surface").prefetch_related("biotope_type")
after_states = comp.after_states.all().order_by("-surface").prefetch_related("biotope_type")
actions = comp.actions.all().prefetch_related("action_type")
context = {
"obj": comp,
"qrcode": qrcode_img,
"qrcode_lanis": qrcode_img_lanis,
"has_access": False, # disables action buttons during rendering
"before_states": before_states,
"after_states": after_states,
"geom_form": geom_form,
"actions": actions,
}
context = BaseContext(request, context).context
return render(request, template, context)

View File

@ -24,6 +24,7 @@ from konova.decorators import any_group_check, default_group_required, conservat
from konova.forms import RemoveModalForm, SimpleGeomForm, NewDocumentForm, RecordModalForm from konova.forms import RemoveModalForm, SimpleGeomForm, NewDocumentForm, RecordModalForm
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
from konova.utils.documents import get_document, remove_document from konova.utils.documents import get_document, remove_document
from konova.utils.generators import generate_qr_code
from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID
from konova.utils.user_checks import in_group from konova.utils.user_checks import in_group
@ -41,7 +42,6 @@ def index_view(request: HttpRequest):
A rendered view A rendered view
""" """
template = "generic_index.html" template = "generic_index.html"
user = request.user
eco_accounts = EcoAccount.objects.filter( eco_accounts = EcoAccount.objects.filter(
deleted=None, deleted=None,
) )
@ -84,7 +84,7 @@ def new_view(request: HttpRequest):
) )
) )
messages.success(request, _("Eco-Account {} added").format(acc.identifier)) messages.success(request, _("Eco-Account {} added").format(acc.identifier))
return redirect("compensation:acc-open", id=acc.id) return redirect("compensation:acc-detail", id=acc.id)
else: else:
messages.error(request, FORM_INVALID) messages.error(request, FORM_INVALID)
else: else:
@ -139,7 +139,7 @@ def edit_view(request: HttpRequest, id: str):
# The data form takes the geom form for processing, as well as the performing user # The data form takes the geom form for processing, as well as the performing user
acc = data_form.save(request.user, geom_form) acc = data_form.save(request.user, geom_form)
messages.success(request, _("Eco-Account {} edited").format(acc.identifier)) messages.success(request, _("Eco-Account {} edited").format(acc.identifier))
return redirect("compensation:acc-open", id=acc.id) return redirect("compensation:acc-detail", id=acc.id)
else: else:
messages.error(request, FORM_INVALID) messages.error(request, FORM_INVALID)
else: else:
@ -155,7 +155,7 @@ def edit_view(request: HttpRequest, id: str):
@login_required @login_required
@any_group_check @any_group_check
def open_view(request: HttpRequest, id: str): def detail_view(request: HttpRequest, id: str):
""" Renders a detail view for a compensation """ Renders a detail view for a compensation
Args: Args:
@ -166,27 +166,36 @@ def open_view(request: HttpRequest, id: str):
""" """
template = "compensation/detail/eco_account/view.html" template = "compensation/detail/eco_account/view.html"
acc = get_object_or_404(EcoAccount, id=id) acc = get_object_or_404(
EcoAccount.objects.prefetch_related(
"deadlines",
).select_related(
'geometry',
'responsible',
),
id=id
)
geom_form = SimpleGeomForm(instance=acc) geom_form = SimpleGeomForm(instance=acc)
_user = request.user _user = request.user
is_data_shared = acc.is_shared_with(_user) is_data_shared = acc.is_shared_with(_user)
# Order states according to surface # Order states according to surface
before_states = acc.before_states.all().order_by("-surface") before_states = acc.before_states.order_by("-surface")
after_states = acc.after_states.all().order_by("-surface") after_states = acc.after_states.order_by("-surface")
# Precalculate logical errors between before- and after-states # Precalculate logical errors between before- and after-states
# Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling # Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling
sum_before_states = before_states.aggregate(Sum("surface"))["surface__sum"] or 0 sum_before_states = before_states.aggregate(Sum("surface"))["surface__sum"] or 0
sum_after_states = after_states.aggregate(Sum("surface"))["surface__sum"] or 0 sum_after_states = after_states.aggregate(Sum("surface"))["surface__sum"] or 0
diff_states = abs(sum_before_states - sum_after_states) diff_states = abs(sum_before_states - sum_after_states)
# Calculate rest of available surface for deductions # Calculate rest of available surface for deductions
available_total, available_relative = acc.get_available_rest() available_total, available_relative = acc.get_available_rest()
# Prefetch related data to decrease the amount of db connections
deductions = acc.deductions.filter( deductions = acc.deductions.filter(
intervention__deleted=None, intervention__deleted=None,
) )
actions = acc.actions.all()
context = { context = {
"obj": acc, "obj": acc,
@ -204,6 +213,7 @@ def open_view(request: HttpRequest, id: str):
"is_ets_member": in_group(_user, ETS_GROUP), "is_ets_member": in_group(_user, ETS_GROUP),
"LANIS_LINK": acc.get_LANIS_link(), "LANIS_LINK": acc.get_LANIS_link(),
"deductions": deductions, "deductions": deductions,
"actions": actions,
} }
context = BaseContext(request, context).context context = BaseContext(request, context).context
return render(request, template, context) return render(request, template, context)
@ -432,3 +442,60 @@ def new_deduction_view(request: HttpRequest, id: str):
request, request,
msg_success=_("Deduction added") msg_success=_("Deduction added")
) )
def report_view(request:HttpRequest, id: str):
""" Renders the public report view
Args:
request (HttpRequest): The incoming request
id (str): The id of the intervention
Returns:
"""
# Reuse the compensation report template since EcoAccounts are structurally identical
template = "compensation/report/eco_account/report.html"
acc = get_object_or_404(EcoAccount, id=id)
# If intervention is not recorded (yet or currently) we need to render another template without any data
if not acc.recorded:
template = "report/unavailable.html"
return render(request, template, {})
# Prepare data for map viewer
geom_form = SimpleGeomForm(
instance=acc
)
qrcode_img = generate_qr_code(
request.build_absolute_uri(reverse("ema:report", args=(id,))),
10
)
qrcode_img_lanis = generate_qr_code(
acc.get_LANIS_link(),
7
)
# Order states by surface
before_states = acc.before_states.all().order_by("-surface").select_related("biotope_type__parent")
after_states = acc.after_states.all().order_by("-surface").select_related("biotope_type__parent")
actions = acc.actions.all().select_related("action_type__parent")
# Reduce amount of db fetched data to the bare minimum we need in the template (deduction's intervention id and identifier)
deductions = acc.deductions.all()\
.distinct("intervention")\
.select_related("intervention")\
.values_list("intervention__id", "intervention__identifier", named=True)
context = {
"obj": acc,
"qrcode": qrcode_img,
"qrcode_lanis": qrcode_img_lanis,
"has_access": False, # disables action buttons during rendering
"before_states": before_states,
"after_states": after_states,
"geom_form": geom_form,
"actions": actions,
"deductions": deductions,
}
context = BaseContext(request, context).context
return render(request, template, context)

View File

@ -99,7 +99,7 @@ class EditEmaForm(NewEmaForm):
self.form_title = _("Edit EMA") self.form_title = _("Edit EMA")
self.action_url = reverse("ema:edit", args=(self.instance.id,)) self.action_url = reverse("ema:edit", args=(self.instance.id,))
self.cancel_redirect = reverse("ema:open", args=(self.instance.id,)) self.cancel_redirect = reverse("ema:detail", args=(self.instance.id,))
self.fields["identifier"].widget.attrs["url"] = reverse_lazy("ema:new-id") self.fields["identifier"].widget.attrs["url"] = reverse_lazy("ema:new-id")
self.fields["title"].widget.attrs["placeholder"] = _("Compensation XY; Location ABC") self.fields["title"].widget.attrs["placeholder"] = _("Compensation XY; Location ABC")

21
ema/managers.py Normal file
View File

@ -0,0 +1,21 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 14.10.21
"""
from django.db import models
class EmaManager(models.Manager):
""" Holds default db fetch setting for this model type
"""
def get_queryset(self):
return super().get_queryset().select_related(
"modified",
"modified__user",
"recorded",
"recorded__user",
)

View File

@ -5,6 +5,7 @@ from django.db import models
from django.db.models import QuerySet from django.db.models import QuerySet
from compensation.models import AbstractCompensation from compensation.models import AbstractCompensation
from ema.managers import EmaManager
from konova.models import AbstractDocument, generate_document_file_upload_path from konova.models import AbstractDocument, generate_document_file_upload_path
from konova.settings import DEFAULT_SRID_RLP, LANIS_LINK_TEMPLATE, EMA_DOC_PATH from konova.settings import DEFAULT_SRID_RLP, LANIS_LINK_TEMPLATE, EMA_DOC_PATH
from user.models import UserActionLogEntry from user.models import UserActionLogEntry
@ -43,6 +44,8 @@ class Ema(AbstractCompensation):
related_name="+" related_name="+"
) )
objects = EmaManager()
def __str__(self): def __str__(self):
return "{}".format(self.identifier) return "{}".format(self.identifier)

View File

@ -49,7 +49,7 @@ class EmaTable(BaseTable):
lm = tables.Column( lm = tables.Column(
verbose_name=_("Last edit"), verbose_name=_("Last edit"),
orderable=True, orderable=True,
accessor="created__timestamp", accessor="modified__timestamp",
) )
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
@ -80,7 +80,7 @@ class EmaTable(BaseTable):
html = "" html = ""
html += self.render_link( html += self.render_link(
tooltip=_("Open {}").format(_("EMA")), tooltip=_("Open {}").format(_("EMA")),
href=reverse("ema:open", args=(record.id,)), href=reverse("ema:detail", args=(record.id,)),
txt=value, txt=value,
new_tab=False, new_tab=False,
) )
@ -122,7 +122,7 @@ class EmaTable(BaseTable):
""" """
html = "" html = ""
has_access = value.filter( has_access = value.filter(
username=self.user.username id=self.user.id
).exists() ).exists()
html += self.render_icn( html += self.render_icn(

View File

@ -6,7 +6,7 @@
LANIS LANIS
</button> </button>
</a> </a>
<a href="{% url 'home' %}" class="mr-2"> <a href="{% url 'ema:report' obj.id %}" target="_blank" class="mr-2">
<button class="btn btn-default" title="{% trans 'Public report' %}"> <button class="btn btn-default" title="{% trans 'Public report' %}">
{% fa5_icon 'file-alt' %} {% fa5_icon 'file-alt' %}
</button> </button>

View File

@ -47,7 +47,7 @@
<td class="align-middle">{{obj.responsible.conservation_file_number|default_if_none:""}}</td> <td class="align-middle">{{obj.responsible.conservation_file_number|default_if_none:""}}</td>
</tr> </tr>
<tr {% if not obj.responsible.handler %}class="alert alert-danger" title="{% trans 'Missing' %}" {% endif %}> <tr {% if not obj.responsible.handler %}class="alert alert-danger" title="{% trans 'Missing' %}" {% endif %}>
<th scope="row">{% trans 'Intervention handler' %}</th> <th scope="row">{% trans 'Action handler' %}</th>
<td class="align-middle">{{obj.responsible.handler|default_if_none:""}}</td> <td class="align-middle">{{obj.responsible.handler|default_if_none:""}}</td>
</tr> </tr>
<tr> <tr>

View File

@ -0,0 +1,71 @@
{% extends 'public_base.html' %}
{% load i18n fontawesome_5 humanize %}
{% block body %}
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<h3>{% trans 'Report' %}</h3>
<h4>{{obj.identifier}}</h4>
<div class="table-container">
<table class="table table-hover">
<tr>
<th class="w-25" scope="row">{% trans 'Title' %}</th>
<td class="align-middle">{{obj.title|default_if_none:""}}</td>
</tr>
<tr>
<th scope="row">{% trans 'Conservation office' %}</th>
<td class="align-middle">{{obj.responsible.conservation_office.str_as_office|default_if_none:""}}</td>
</tr>
<tr>
<th scope="row">{% trans 'Conservation office file number' %}</th>
<td class="align-middle">{{obj.responsible.conservation_file_number|default_if_none:""}}</td>
</tr>
<tr>
<th scope="row">{% trans 'Action handler' %}</th>
<td class="align-middle">{{obj.responsible.handler|default_if_none:""}}</td>
</tr>
<tr>
<th scope="row">{% trans 'Funded by' %}</th>
<td class="align-middle">
{% with obj.fundings.all as fundings %}
{% for funding in fundings %}
<div class="badge pill-badge rlp-r-outline">{{funding.short_name}}</div>
<br>
{% empty %}
{% trans 'None' %}
{% endfor %}
{% endwith %}
</td>
</tr>
<tr>
<th scope="row">{% trans 'Last modified' %}</th>
<td class="align-middle">
{{obj.modified.timestamp|default_if_none:""|naturalday}}
</td>
</tr>
</table>
</div>
{% include 'compensation/detail/compensation/includes/states-before.html' %}
{% include 'compensation/detail/compensation/includes/states-after.html' %}
{% include 'compensation/detail/compensation/includes/actions.html' %}
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="row">
{% include 'map/geom_form.html' %}
</div>
<div class="row">
<div class="col-sm-6 col-md-6 col-lg-6">
<h4>{% trans 'Open in browser' %}</h4>
{{ qrcode|safe }}
</div>
<div class="col-sm-6 col-md-6 col-lg-6">
<h4>{% trans 'View in LANIS' %}</h4>
{{ qrcode_lanis|safe }}
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -13,11 +13,12 @@ urlpatterns = [
path("", index_view, name="index"), path("", index_view, name="index"),
path("new/", new_view, name="new"), path("new/", new_view, name="new"),
path("new/id", new_id_view, name="new-id"), path("new/id", new_id_view, name="new-id"),
path("<id>", open_view, name="open"), path("<id>", detail_view, name="detail"),
path('<id>/log', log_view, name='log'), path('<id>/log', log_view, name='log'),
path('<id>/edit', edit_view, name='edit'), path('<id>/edit', edit_view, name='edit'),
path('<id>/remove', remove_view, name='remove'), path('<id>/remove', remove_view, name='remove'),
path('<id>/record', record_view, name='record'), path('<id>/record', record_view, name='record'),
path('<id>/report', report_view, name='report'),
path('<id>/state/new', state_new_view, name='new-state'), path('<id>/state/new', state_new_view, name='new-state'),
path('<id>/action/new', action_new_view, name='new-action'), path('<id>/action/new', action_new_view, name='new-action'),
path('<id>/deadline/new', deadline_new_view, name="new-deadline"), path('<id>/deadline/new', deadline_new_view, name="new-deadline"),

View File

@ -16,6 +16,7 @@ from ema.models import Ema, EmaDocument
from konova.forms import RemoveModalForm, NewDocumentForm, SimpleGeomForm, RecordModalForm from konova.forms import RemoveModalForm, NewDocumentForm, SimpleGeomForm, RecordModalForm
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
from konova.utils.documents import get_document, remove_document from konova.utils.documents import get_document, remove_document
from konova.utils.generators import generate_qr_code
from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID
from konova.utils.user_checks import in_group from konova.utils.user_checks import in_group
@ -75,7 +76,7 @@ def new_view(request: HttpRequest):
) )
) )
messages.success(request, _("EMA {} added").format(ema.identifier)) messages.success(request, _("EMA {} added").format(ema.identifier))
return redirect("ema:open", id=ema.id) return redirect("ema:detail", id=ema.id)
else: else:
messages.error(request, FORM_INVALID) messages.error(request, FORM_INVALID)
else: else:
@ -108,7 +109,7 @@ def new_id_view(request: HttpRequest):
@login_required @login_required
def open_view(request: HttpRequest, id: str): def detail_view(request: HttpRequest, id: str):
""" Renders the detail view of an EMA """ Renders the detail view of an EMA
Args: Args:
@ -199,7 +200,7 @@ def edit_view(request: HttpRequest, id: str):
# The data form takes the geom form for processing, as well as the performing user # The data form takes the geom form for processing, as well as the performing user
ema = data_form.save(request.user, geom_form) ema = data_form.save(request.user, geom_form)
messages.success(request, _("EMA {} edited").format(ema.identifier)) messages.success(request, _("EMA {} edited").format(ema.identifier))
return redirect("ema:open", id=ema.id) return redirect("ema:detail", id=ema.id)
else: else:
messages.error(request, FORM_INVALID) messages.error(request, FORM_INVALID)
else: else:
@ -399,3 +400,52 @@ def action_remove_view(request: HttpRequest, id: str):
id id
) )
def report_view(request:HttpRequest, id: str):
""" Renders the public report view
Args:
request (HttpRequest): The incoming request
id (str): The id of the intervention
Returns:
"""
# Reuse the compensation report template since EMAs are structurally identical
template = "ema/report/report.html"
ema = get_object_or_404(Ema, id=id)
# If intervention is not recorded (yet or currently) we need to render another template without any data
if not ema.recorded:
template = "report/unavailable.html"
return render(request, template, {})
# Prepare data for map viewer
geom_form = SimpleGeomForm(
instance=ema
)
qrcode_img = generate_qr_code(
request.build_absolute_uri(reverse("ema:report", args=(id,))),
10
)
qrcode_img_lanis = generate_qr_code(
ema.get_LANIS_link(),
7
)
# Order states by surface
before_states = ema.before_states.all().order_by("-surface").prefetch_related("biotope_type")
after_states = ema.after_states.all().order_by("-surface").prefetch_related("biotope_type")
actions = ema.actions.all().prefetch_related("action_type")
context = {
"obj": ema,
"qrcode": qrcode_img,
"qrcode_lanis": qrcode_img_lanis,
"has_access": False, # disables action buttons during rendering
"before_states": before_states,
"after_states": after_states,
"geom_form": geom_form,
"actions": actions,
}
context = BaseContext(request, context).context
return render(request, template, context)

View File

@ -269,7 +269,7 @@ class EditInterventionForm(NewInterventionForm):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
if self.instance is not None: if self.instance is not None:
self.action_url = reverse("intervention:edit", args=(self.instance.id,)) self.action_url = reverse("intervention:edit", args=(self.instance.id,))
self.cancel_redirect = reverse("intervention:open", args=(self.instance.id,)) self.cancel_redirect = reverse("intervention:detail", args=(self.instance.id,))
self.form_title = _("Edit intervention") self.form_title = _("Edit intervention")
self.form_caption = "" self.form_caption = ""

48
intervention/managers.py Normal file
View File

@ -0,0 +1,48 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 14.10.21
"""
from django.db import models
class InterventionManager(models.Manager):
""" Holds default db fetch setting for this model type
"""
def get_queryset(self):
return super().get_queryset().select_related(
"recorded",
"recorded__user",
"modified",
"modified__user",
"checked",
"checked__user",
).prefetch_related(
"users",
)
class LegalDataManager(models.Manager):
""" Holds default db fetch setting for this model type
"""
def get_queryset(self):
return super().get_querset().select_related(
"process_type",
).prefetch_related(
"laws"
)
class ResponsibilityDataManager(models.Manager):
""" Holds default db fetch setting for this model type
"""
def get_queryset(self):
return super().get_querset().select_related(
"registration_office",
"conservation_office",
)

View File

@ -10,16 +10,15 @@ import shutil
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.gis.db import models from django.contrib.gis.db import models
from django.db.models import QuerySet from django.db.models import QuerySet
from django.utils.timezone import localtime
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from codelist.models import KonovaCode from codelist.models import KonovaCode
from codelist.settings import CODELIST_REGISTRATION_OFFICE_ID, CODELIST_CONSERVATION_OFFICE_ID, CODELIST_LAW_ID, \ from codelist.settings import CODELIST_REGISTRATION_OFFICE_ID, CODELIST_CONSERVATION_OFFICE_ID, CODELIST_LAW_ID, \
CODELIST_PROCESS_TYPE_ID CODELIST_PROCESS_TYPE_ID
from intervention.managers import InterventionManager, LegalDataManager, ResponsibilityDataManager
from konova.models import BaseObject, Geometry, UuidModel, BaseResource, AbstractDocument, \ from konova.models import BaseObject, Geometry, UuidModel, BaseResource, AbstractDocument, \
generate_document_file_upload_path generate_document_file_upload_path
from konova.settings import DEFAULT_SRID_RLP, LANIS_LINK_TEMPLATE from konova.settings import DEFAULT_SRID_RLP, LANIS_LINK_TEMPLATE, LANIS_ZOOM_LUT
from konova.sub_settings.django_settings import DEFAULT_DATE_TIME_FORMAT
from konova.utils import generators from konova.utils import generators
from user.models import UserActionLogEntry from user.models import UserActionLogEntry
@ -57,6 +56,8 @@ class ResponsibilityData(UuidModel):
conservation_file_number = models.CharField(max_length=1000, blank=True, null=True) conservation_file_number = models.CharField(max_length=1000, blank=True, null=True)
handler = models.CharField(max_length=500, null=True, blank=True, help_text="Refers to 'Eingriffsverursacher' or 'Maßnahmenträger'") handler = models.CharField(max_length=500, null=True, blank=True, help_text="Refers to 'Eingriffsverursacher' or 'Maßnahmenträger'")
objects = ResponsibilityDataManager()
def __str__(self): def __str__(self):
return "ZB: {} | ETS: {} | Handler: {}".format( return "ZB: {} | ETS: {} | Handler: {}".format(
self.registration_office, self.registration_office,
@ -171,6 +172,8 @@ class LegalData(UuidModel):
revocation = models.OneToOneField(Revocation, null=True, blank=True, help_text="Refers to 'Widerspruch am'", on_delete=models.SET_NULL) revocation = models.OneToOneField(Revocation, null=True, blank=True, help_text="Refers to 'Widerspruch am'", on_delete=models.SET_NULL)
objects = LegalDataManager()
class Intervention(BaseObject): class Intervention(BaseObject):
""" """
@ -221,6 +224,8 @@ class Intervention(BaseObject):
help_text="Used for sharing access", help_text="Used for sharing access",
) )
objects = InterventionManager()
def __str__(self): def __str__(self):
return "{} ({})".format(self.identifier, self.title) return "{} ({})".format(self.identifier, self.title)
@ -361,7 +366,13 @@ class Intervention(BaseObject):
geom = self.geometry.geom.transform(DEFAULT_SRID_RLP, clone=True) geom = self.geometry.geom.transform(DEFAULT_SRID_RLP, clone=True)
x = geom.centroid.x x = geom.centroid.x
y = geom.centroid.y y = geom.centroid.y
zoom_lvl = 16 area = int(geom.envelope.area)
z_l = 16
for k_area, v_zoom in LANIS_ZOOM_LUT.items():
if k_area < area:
z_l = v_zoom
break
zoom_lvl = z_l
except AttributeError: except AttributeError:
# If no geometry has been added, yet. # If no geometry has been added, yet.
x = 1 x = 1
@ -373,16 +384,6 @@ class Intervention(BaseObject):
y, y,
) )
@property
def recorded_tooltip(self):
tooltip = _("Not recorded yet")
if self.recorded:
value = self.recorded.timestamp
value = localtime(value)
on = value.strftime(DEFAULT_DATE_TIME_FORMAT)
tooltip = _("Recorded on {} by {}").format(on, self.recorded.user)
return tooltip
def get_documents(self) -> (QuerySet, QuerySet): def get_documents(self) -> (QuerySet, QuerySet):
""" Getter for all documents of an intervention """ Getter for all documents of an intervention

View File

@ -86,7 +86,7 @@ class InterventionTable(BaseTable):
html = "" html = ""
html += self.render_link( html += self.render_link(
tooltip=_("Open {}").format(_("Intervention")), tooltip=_("Open {}").format(_("Intervention")),
href=reverse("intervention:open", args=(record.id,)), href=reverse("intervention:detail", args=(record.id,)),
txt=value, txt=value,
new_tab=False, new_tab=False,
) )
@ -117,7 +117,7 @@ class InterventionTable(BaseTable):
return format_html(html) return format_html(html)
def render_r(self, value, record: Intervention): def render_r(self, value, record: Intervention):
""" Renders the registered column for an intervention """ Renders the recorded column for an intervention
Args: Args:
value (str): The identifier value value (str): The identifier value
@ -141,7 +141,7 @@ class InterventionTable(BaseTable):
return format_html(html) return format_html(html)
def render_e(self, value, record: Intervention): def render_e(self, value, record: Intervention):
""" Renders the registered column for an intervention """ Renders the editable column for an intervention
Args: Args:
value (str): The identifier value value (str): The identifier value
@ -152,7 +152,7 @@ class InterventionTable(BaseTable):
""" """
html = "" html = ""
has_access = value.filter( has_access = value.filter(
username=self.user.username id=self.user.id
).exists() ).exists()
html += self.render_icn( html += self.render_icn(

View File

@ -1,23 +0,0 @@
{% load i18n fontawesome_5 %}
{% if intervention.comment %}
<div class="w-100">
<div class="card mt-3">
<div class="card-header rlp-gd">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-12">
<h5 class="card-title">
{% fa5_icon 'info-circle' %}
{% trans 'Comment' %}
</h5>
</div>
</div>
</div>
<div class="card-body">
<div class="card-text font-italic">
{{intervention.comment}}
</div>
</div>
</div>
</div>
{% endif %}

View File

@ -11,7 +11,7 @@
<div class="col-sm-6"> <div class="col-sm-6">
<div class="d-flex justify-content-end"> <div class="d-flex justify-content-end">
{% if is_default_member and has_access %} {% if is_default_member and has_access %}
<a href="{% url 'compensation:new' intervention.id %}" title="{% trans 'Add new compensation' %}"> <a href="{% url 'compensation:new' obj.id %}" title="{% trans 'Add new compensation' %}">
<button class="btn btn-outline-default"> <button class="btn btn-outline-default">
{% fa5_icon 'plus' %} {% fa5_icon 'plus' %}
{% fa5_icon 'leaf' %} {% fa5_icon 'leaf' %}
@ -32,16 +32,18 @@
<th scope="col"> <th scope="col">
{% trans 'Title' %} {% trans 'Title' %}
</th> </th>
{% if is_default_member and has_access %}
<th scope="col"> <th scope="col">
{% trans 'Action' %} {% trans 'Action' %}
</th> </th>
{% endif %}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for comp in compensations %} {% for comp in compensations %}
<tr> <tr>
<td class="align-middle"> <td class="align-middle">
<a href="{% url 'compensation:open' comp.id %}"> <a href="{% url 'compensation:detail' comp.id %}">
{{ comp.identifier }} {{ comp.identifier }}
</a> </a>
</td> </td>

View File

@ -6,41 +6,41 @@
LANIS LANIS
</button> </button>
</a> </a>
<a href="{% url 'home' %}" class="mr-2"> <a href="{% url 'intervention:report' obj.id %}" target="_blank" class="mr-2" target="_blank">
<button class="btn btn-default" title="{% trans 'Public report' %}"> <button class="btn btn-default" title="{% trans 'Public report' %}">
{% fa5_icon 'file-alt' %} {% fa5_icon 'file-alt' %}
</button> </button>
</a> </a>
{% if has_access %} {% if has_access %}
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Share' %}" data-form-url="{% url 'intervention:share-create' intervention.id %}"> <button class="btn btn-default btn-modal mr-2" title="{% trans 'Share' %}" data-form-url="{% url 'intervention:share-create' obj.id %}">
{% fa5_icon 'share-alt' %} {% fa5_icon 'share-alt' %}
</button> </button>
{% if is_zb_member %} {% if is_zb_member %}
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Run check' %}" data-form-url="{% url 'intervention:run-check' intervention.id %}"> <button class="btn btn-default btn-modal mr-2" title="{% trans 'Run check' %}" data-form-url="{% url 'intervention:run-check' obj.id %}">
{% fa5_icon 'star' %} {% fa5_icon 'star' %}
</button> </button>
{% endif %} {% endif %}
{% if is_ets_member %} {% if is_ets_member %}
{% if intervention.recorded %} {% if obj.recorded %}
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Unrecord' %}" data-form-url="{% url 'intervention:record' intervention.id %}"> <button class="btn btn-default btn-modal mr-2" title="{% trans 'Unrecord' %}" data-form-url="{% url 'intervention:record' obj.id %}">
{% fa5_icon 'bookmark' 'far' %} {% fa5_icon 'bookmark' 'far' %}
</button> </button>
{% else %} {% else %}
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Record' %}" data-form-url="{% url 'intervention:record' intervention.id %}"> <button class="btn btn-default btn-modal mr-2" title="{% trans 'Record' %}" data-form-url="{% url 'intervention:record' obj.id %}">
{% fa5_icon 'bookmark' %} {% fa5_icon 'bookmark' %}
</button> </button>
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if is_default_member %} {% if is_default_member %}
<a href="{% url 'intervention:edit' intervention.id %}" class="mr-2"> <a href="{% url 'intervention:edit' obj.id %}" class="mr-2">
<button class="btn btn-default" title="{% trans 'Edit' %}"> <button class="btn btn-default" title="{% trans 'Edit' %}">
{% fa5_icon 'edit' %} {% fa5_icon 'edit' %}
</button> </button>
</a> </a>
<button class="btn btn-default btn-modal mr-2" data-form-url="{% url 'intervention:log' intervention.id %}" title="{% trans 'Show log' %}"> <button class="btn btn-default btn-modal mr-2" data-form-url="{% url 'intervention:log' obj.id %}" title="{% trans 'Show log' %}">
{% fa5_icon 'history' %} {% fa5_icon 'history' %}
</button> </button>
<button class="btn btn-default btn-modal" data-form-url="{% url 'intervention:remove' intervention.id %}" title="{% trans 'Delete' %}"> <button class="btn btn-default btn-modal" data-form-url="{% url 'intervention:remove' obj.id %}" title="{% trans 'Delete' %}">
{% fa5_icon 'trash' %} {% fa5_icon 'trash' %}
</button> </button>
{% endif %} {% endif %}

View File

@ -4,14 +4,14 @@
<div class="row"> <div class="row">
<div class="col-sm-6"> <div class="col-sm-6">
<h5> <h5>
<span class="badge badge-light">{{intervention.deductions.count}}</span> <span class="badge badge-light">{{obj.deductions.count}}</span>
{% trans 'Eco Account Deductions' %} {% trans 'Eco Account Deductions' %}
</h5> </h5>
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<div class="d-flex justify-content-end"> <div class="d-flex justify-content-end">
{% if is_default_member and has_access %} {% if is_default_member and has_access %}
<button class="btn btn-outline-default btn-modal" data-form-url="{% url 'intervention:acc-new-deduction' intervention.id %}" title="{% trans 'Add new deduction' %}"> <button class="btn btn-outline-default btn-modal" data-form-url="{% url 'intervention:acc-new-deduction' obj.id %}" title="{% trans 'Add new deduction' %}">
{% fa5_icon 'plus' %} {% fa5_icon 'plus' %}
{% fa5_icon 'tree' %} {% fa5_icon 'tree' %}
</button> </button>
@ -33,16 +33,18 @@
<th scope="col"> <th scope="col">
{% trans 'Created' %} {% trans 'Created' %}
</th> </th>
{% if is_default_member and has_access %}
<th scope="col"> <th scope="col">
{% trans 'Action' %} {% trans 'Action' %}
</th> </th>
{% endif %}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for deduction in intervention.deductions.all %} {% for deduction in obj.deductions.all %}
<tr {% if deduction.account.deleted %}class="align-middle alert-danger" title="{% trans 'Eco-account deleted! Deduction invalid!' %}" {% elif not deduction.account.recorded %}class="align-middle alert-danger" title="{% trans 'Eco-account not recorded! Deduction invalid!' %}" {% endif %}> <tr {% if deduction.account.deleted %}class="align-middle alert-danger" title="{% trans 'Eco-account deleted! Deduction invalid!' %}" {% elif not deduction.account.recorded %}class="align-middle alert-danger" title="{% trans 'Eco-account not recorded! Deduction invalid!' %}" {% endif %}>
<td class="align-middle"> <td class="align-middle">
<a href="{% url 'compensation:acc-open' deduction.account.id %}"> <a href="{% url 'compensation:acc-detail' deduction.account.id %}">
{% if deduction.account.deleted or not deduction.account.recorded %} {% if deduction.account.deleted or not deduction.account.recorded %}
{% fa5_icon 'exclamation-triangle' %} {% fa5_icon 'exclamation-triangle' %}
{% endif %} {% endif %}

View File

@ -4,14 +4,14 @@
<div class="row"> <div class="row">
<div class="col-sm-6"> <div class="col-sm-6">
<h5> <h5>
<span class="badge badge-light">{{intervention.documents.count}}</span> <span class="badge badge-light">{{obj.documents.count}}</span>
{% trans 'Documents' %} {% trans 'Documents' %}
</h5> </h5>
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<div class="d-flex justify-content-end"> <div class="d-flex justify-content-end">
{% if is_default_member and has_access %} {% if is_default_member and has_access %}
<button class="btn btn-outline-default btn-modal" data-form-url="{% url 'intervention:new-doc' intervention.id %}" title="{% trans 'Add new document' %}"> <button class="btn btn-outline-default btn-modal" data-form-url="{% url 'intervention:new-doc' obj.id %}" title="{% trans 'Add new document' %}">
{% fa5_icon 'plus' %} {% fa5_icon 'plus' %}
{% fa5_icon 'file' %} {% fa5_icon 'file' %}
</button> </button>
@ -30,13 +30,15 @@
<th scope="col"> <th scope="col">
{% trans 'Comment' %} {% trans 'Comment' %}
</th> </th>
{% if is_default_member and has_access %}
<th scope="col"> <th scope="col">
{% trans 'Action' %} {% trans 'Action' %}
</th> </th>
{% endif %}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for doc in intervention.documents.all %} {% for doc in obj.documents.all %}
<tr> <tr>
<td class="align-middle"> <td class="align-middle">
<a href="{% url 'intervention:get-doc' doc.id %}"> <a href="{% url 'intervention:get-doc' doc.id %}">

View File

@ -4,14 +4,14 @@
<div class="row"> <div class="row">
<div class="col-sm-6"> <div class="col-sm-6">
<h5> <h5>
<span class="badge badge-light">{{intervention.payments.count}}</span> <span class="badge badge-light">{{obj.payments.count}}</span>
{% trans 'Payments' %} {% trans 'Payments' %}
</h5> </h5>
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<div class="d-flex justify-content-end"> <div class="d-flex justify-content-end">
{% if is_default_member and has_access %} {% if is_default_member and has_access %}
<button class="btn btn-outline-default btn-modal" data-form-url="{% url 'compensation:pay-new' intervention.id %}" title="{% trans 'Add new payment' %}"> <button class="btn btn-outline-default btn-modal" data-form-url="{% url 'compensation:pay-new' obj.id %}" title="{% trans 'Add new payment' %}">
{% fa5_icon 'plus' %} {% fa5_icon 'plus' %}
{% fa5_icon 'money-bill-wave' %} {% fa5_icon 'money-bill-wave' %}
</button> </button>
@ -33,13 +33,15 @@
<th scope="col"> <th scope="col">
{% trans 'Comment' %} {% trans 'Comment' %}
</th> </th>
{% if is_default_member and has_access %}
<th scope="col"> <th scope="col">
{% trans 'Action' %} {% trans 'Action' %}
</th> </th>
{% endif %}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for pay in intervention.payments.all %} {% for pay in obj.payments.all %}
<tr> <tr>
<td class="align-middle"> <td class="align-middle">
{{ pay.amount|floatformat:2 }} € {{ pay.amount|floatformat:2 }} €

View File

@ -4,7 +4,7 @@
<div class="row"> <div class="row">
<div class="col-sm-6"> <div class="col-sm-6">
<h5> <h5>
<span class="badge badge-light">{% if intervention.legal.revocation %}1{% else %}0{% endif %}</span> <span class="badge badge-light">{% if obj.legal.revocation %}1{% else %}0{% endif %}</span>
{% trans 'Revocation' %} {% trans 'Revocation' %}
</h5> </h5>
</div> </div>
@ -13,8 +13,8 @@
{% comment %} {% comment %}
Only show add-button if no revocation exists, yet. Only show add-button if no revocation exists, yet.
{% endcomment %} {% endcomment %}
{% if is_default_member and has_access and not intervention.legal.revocation %} {% if is_default_member and has_access and not obj.legal.revocation %}
<button class="btn btn-outline-default btn-modal" data-form-url="{% url 'intervention:new-revocation' intervention.id %}" title="{% trans 'Add revocation' %}"> <button class="btn btn-outline-default btn-modal" data-form-url="{% url 'intervention:new-revocation' obj.id %}" title="{% trans 'Add revocation' %}">
{% fa5_icon 'plus' %} {% fa5_icon 'plus' %}
{% fa5_icon 'ban' %} {% fa5_icon 'ban' %}
</button> </button>
@ -36,14 +36,16 @@
<th scope="col"> <th scope="col">
{% trans 'Comment' %} {% trans 'Comment' %}
</th> </th>
{% if is_default_member and has_access %}
<th scope="col"> <th scope="col">
{% trans 'Action' %} {% trans 'Action' %}
</th> </th>
{% endif %}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% if intervention.legal.revocation %} {% if obj.legal.revocation %}
{% with intervention.legal.revocation as rev %} {% with obj.legal.revocation as rev %}
<tr> <tr>
<td class="align-middle"> <td class="align-middle">
{{ rev.date }} {{ rev.date }}

View File

@ -15,7 +15,7 @@
<div id="detail-header" class="row"> <div id="detail-header" class="row">
<div class="col-sm-12 col-md-12 col-lg-6"> <div class="col-sm-12 col-md-12 col-lg-6">
<h3>{% trans 'Intervention' %}<br> {{intervention.identifier}}</h3> <h3>{% trans 'Intervention' %}<br> {{obj.identifier}}</h3>
</div> </div>
<div class="col-sm-12 col-md-12 col-lg-6"> <div class="col-sm-12 col-md-12 col-lg-6">
{% include 'intervention/detail/includes/controls.html' %} {% include 'intervention/detail/includes/controls.html' %}
@ -26,52 +26,52 @@
<div class="col-sm-12 col-md-12 col-lg-6"> <div class="col-sm-12 col-md-12 col-lg-6">
<div class="table-container"> <div class="table-container">
<table class="table table-hover"> <table class="table table-hover">
<tr {% if not intervention.title %}class="alert alert-danger" title="{% trans 'Missing' %}" {% endif %}> <tr {% if not obj.title %}class="alert alert-danger" title="{% trans 'Missing' %}" {% endif %}>
<th class="w-25" scope="row">{% trans 'Title' %}</th> <th class="w-25" scope="row">{% trans 'Title' %}</th>
<td class="align-middle">{{intervention.title|default_if_none:""}}</td> <td class="align-middle">{{obj.title|default_if_none:""}}</td>
</tr> </tr>
<tr {% if not intervention.legal.process_type %}class="alert alert-danger" title="{% trans 'Missing' %}" {% endif %}> <tr {% if not obj.legal.process_type %}class="alert alert-danger" title="{% trans 'Missing' %}" {% endif %}>
<th scope="row">{% trans 'Process type' %}</th> <th scope="row">{% trans 'Process type' %}</th>
<td class="align-middle">{{intervention.legal.process_type|default_if_none:""}}</td> <td class="align-middle">{{obj.legal.process_type|default_if_none:""}}</td>
</tr> </tr>
<tr {% if intervention.legal.laws.count == 0 %}class="alert alert-danger" title="{% trans 'Missing' %}" {% endif %}> <tr {% if obj.legal.laws.count == 0 %}class="alert alert-danger" title="{% trans 'Missing' %}" {% endif %}>
<th scope="row">{% trans 'Law' %}</th> <th scope="row">{% trans 'Law' %}</th>
<td class="align-middle"> <td class="align-middle">
{% for law in intervention.legal.laws.all %} {% for law in obj.legal.laws.all %}
<div class="badge pill-badge rlp-r-outline">{{law.short_name}} - {{law.long_name}}</div> <div class="badge pill-badge rlp-r-outline">{{law.short_name}} - {{law.long_name}}</div>
<br> <br>
{% endfor %} {% endfor %}
</td> </td>
</tr> </tr>
<tr {% if not intervention.responsible.registration_office %}class="alert alert-danger" title="{% trans 'Missing' %}" {% endif %}> <tr {% if not obj.responsible.registration_office %}class="alert alert-danger" title="{% trans 'Missing' %}" {% endif %}>
<th scope="row">{% trans 'Registration office' %}</th> <th scope="row">{% trans 'Registration office' %}</th>
<td class="align-middle">{{intervention.responsible.registration_office.str_as_office|default_if_none:""}}</td> <td class="align-middle">{{obj.responsible.registration_office.str_as_office|default_if_none:""}}</td>
</tr> </tr>
<tr {% if not intervention.responsible.registration_file_number %}class="alert alert-danger" title="{% trans 'Missing' %}" {% endif %}> <tr {% if not obj.responsible.registration_file_number %}class="alert alert-danger" title="{% trans 'Missing' %}" {% endif %}>
<th scope="row">{% trans 'Registration office file number' %}</th> <th scope="row">{% trans 'Registration office file number' %}</th>
<td class="align-middle">{{intervention.responsible.registration_file_number|default_if_none:""}}</td> <td class="align-middle">{{obj.responsible.registration_file_number|default_if_none:""}}</td>
</tr> </tr>
<tr {% if not intervention.responsible.conservation_office %}class="alert alert-danger" title="{% trans 'Missing' %}" {% endif %}> <tr {% if not obj.responsible.conservation_office %}class="alert alert-danger" title="{% trans 'Missing' %}" {% endif %}>
<th scope="row">{% trans 'Conservation office' %}</th> <th scope="row">{% trans 'Conservation office' %}</th>
<td class="align-middle">{{intervention.responsible.conservation_office.str_as_office|default_if_none:""}}</td> <td class="align-middle">{{obj.responsible.conservation_office.str_as_office|default_if_none:""}}</td>
</tr> </tr>
<tr {% if not intervention.responsible.conservation_file_number %}class="alert alert-danger" title="{% trans 'Missing' %}" {% endif %}> <tr {% if not obj.responsible.conservation_file_number %}class="alert alert-danger" title="{% trans 'Missing' %}" {% endif %}>
<th scope="row">{% trans 'Conservation office file number' %}</th> <th scope="row">{% trans 'Conservation office file number' %}</th>
<td class="align-middle">{{intervention.responsible.conservation_file_number|default_if_none:""}}</td> <td class="align-middle">{{obj.responsible.conservation_file_number|default_if_none:""}}</td>
</tr> </tr>
<tr {% if not intervention.responsible.handler %}class="alert alert-danger" title="{% trans 'Missing' %}" {% endif %}> <tr {% if not obj.responsible.handler %}class="alert alert-danger" title="{% trans 'Missing' %}" {% endif %}>
<th scope="row">{% trans 'Intervention handler' %}</th> <th scope="row">{% trans 'Intervention handler' %}</th>
<td class="align-middle">{{intervention.responsible.handler|default_if_none:""}}</td> <td class="align-middle">{{obj.responsible.handler|default_if_none:""}}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">{% trans 'Checked' %}</th> <th scope="row">{% trans 'Checked' %}</th>
<td class="align-middle"> <td class="align-middle">
{% if intervention.checked is None %} {% if obj.checked is None %}
<span> <span>
{% fa5_icon 'star' 'far' %} {% fa5_icon 'star' 'far' %}
</span> </span>
{% else %} {% else %}
<span class="check-star" title="{% trans 'Checked on '%} {{intervention.checked.timestamp}} {% trans 'by' %} {{intervention.checked.user}}"> <span class="check-star" title="{% trans 'Checked on '%} {{obj.checked.timestamp}} {% trans 'by' %} {{obj.checked.user}}">
{% fa5_icon 'star' %} {% fa5_icon 'star' %}
</span> </span>
{% endif %} {% endif %}
@ -80,41 +80,41 @@
<tr> <tr>
<th scope="row">{% trans 'Recorded' %}</th> <th scope="row">{% trans 'Recorded' %}</th>
<td class="align-middle"> <td class="align-middle">
{% if intervention.recorded is None %} {% if obj.recorded is None %}
<span title="{% trans 'Not recorded yet' %}"> <span title="{% trans 'Not recorded yet' %}">
{% fa5_icon 'bookmark' 'far' %} {% fa5_icon 'bookmark' 'far' %}
</span> </span>
{% else %} {% else %}
<span class="registered-bookmark" title="{% trans 'Recorded on '%} {{intervention.recorded.timestamp}} {% trans 'by' %} {{intervention.recorded.user}}"> <span class="registered-bookmark" title="{% trans 'Recorded on '%} {{obj.recorded.timestamp}} {% trans 'by' %} {{obj.recorded.user}}">
{% fa5_icon 'bookmark' %} {% fa5_icon 'bookmark' %}
</span> </span>
{% endif %} {% endif %}
</td> </td>
</tr> </tr>
<tr {% if not intervention.legal.registration_date %}class="alert alert-danger" title="{% trans 'Missing' %}" {% endif %}> <tr {% if not obj.legal.registration_date %}class="alert alert-danger" title="{% trans 'Missing' %}" {% endif %}>
<th scope="row">{% trans 'Registration date' %}</th> <th scope="row">{% trans 'Registration date' %}</th>
<td class="align-middle">{{intervention.legal.registration_date|default_if_none:""}}</td> <td class="align-middle">{{obj.legal.registration_date|default_if_none:""}}</td>
</tr> </tr>
<tr {% if not intervention.legal.binding_date %}class="alert alert-danger" title="{% trans 'Missing' %}" {% endif %}> <tr {% if not obj.legal.binding_date %}class="alert alert-danger" title="{% trans 'Missing' %}" {% endif %}>
<th scope="row">{% trans 'Binding on' %}</th> <th scope="row">{% trans 'Binding on' %}</th>
<td class="align-middle">{{intervention.legal.binding_date|default_if_none:""}}</td> <td class="align-middle">{{obj.legal.binding_date|default_if_none:""}}</td>
</tr> </tr>
<tr {% if intervention.legal.revocation %}class="alert alert-danger" title="{% trans 'Exists' %}" {% endif %}> <tr {% if obj.legal.revocation %}class="alert alert-danger" title="{% trans 'Exists' %}" {% endif %}>
<th scope="row">{% trans 'Revocation' %}</th> <th scope="row">{% trans 'Revocation' %}</th>
<td class="align-middle">{{intervention.legal.revocation.date|naturalday|default_if_none:""}}</td> <td class="align-middle">{{obj.legal.revocation.date|naturalday|default_if_none:""}}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">{% trans 'Last modified' %}</th> <th scope="row">{% trans 'Last modified' %}</th>
<td class="align-middle"> <td class="align-middle">
{{intervention.created.timestamp|default_if_none:""|naturalday}} {{obj.created.timestamp|default_if_none:""|naturalday}}
<br> <br>
{{intervention.created.user.username}} {{obj.created.user.username}}
</td> </td>
</tr> </tr>
<tr> <tr>
<th scope="row">{% trans 'Shared with' %}</th> <th scope="row">{% trans 'Shared with' %}</th>
<td class="align-middle"> <td class="align-middle">
{% for user in intervention.users.all %} {% for user in obj.users.all %}
{% include 'user/includes/contact_modal_button.html' %} {% include 'user/includes/contact_modal_button.html' %}
{% endfor %} {% endfor %}
</td> </td>
@ -127,7 +127,7 @@
{% include 'map/geom_form.html' %} {% include 'map/geom_form.html' %}
</div> </div>
<div class="row"> <div class="row">
{% include 'intervention/detail/includes/comment.html' %} {% include 'konova/comment_card.html' %}
</div> </div>
</div> </div>
</div> </div>

View File

@ -0,0 +1,117 @@
{% extends 'public_base.html' %}
{% load i18n fontawesome_5 humanize %}
{% block body %}
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<h3>{% trans 'Report' %}</h3>
<h4>{{obj.identifier}}</h4>
<div class="table-container">
<table class="table table-hover">
<tr>
<th class="w-25" scope="row">{% trans 'Title' %}</th>
<td class="align-middle">{{obj.title|default_if_none:""}}</td>
</tr>
<tr>
<th scope="row">{% trans 'Process type' %}</th>
<td class="align-middle">{{obj.legal.process_type|default_if_none:""}}</td>
</tr>
<tr>
<th scope="row">{% trans 'Law' %}</th>
<td class="align-middle">
{% for law in obj.legal.laws.all %}
<div class="badge pill-badge rlp-r-outline">{{law.short_name}} - {{law.long_name}}</div>
<br>
{% endfor %}
</td>
</tr>
<tr>
<th scope="row">{% trans 'Registration office' %}</th>
<td class="align-middle">{{obj.responsible.registration_office.str_as_office|default_if_none:""}}</td>
</tr>
<tr>
<th scope="row">{% trans 'Registration office file number' %}</th>
<td class="align-middle">{{obj.responsible.registration_file_number|default_if_none:""}}</td>
</tr>
<tr>
<th scope="row">{% trans 'Conservation office' %}</th>
<td class="align-middle">{{obj.responsible.conservation_office.str_as_office|default_if_none:""}}</td>
</tr>
<tr>
<th scope="row">{% trans 'Conservation office file number' %}</th>
<td class="align-middle">{{obj.responsible.conservation_file_number|default_if_none:""}}</td>
</tr>
<tr>
<th scope="row">{% trans 'Intervention handler' %}</th>
<td class="align-middle">{{obj.responsible.handler|default_if_none:""}}</td>
</tr>
<tr>
<th scope="row">{% trans 'Compensations' %}</th>
<td class="align-middle">
{% for comp in obj.compensations.all %}
<a href="{% url 'compensation:report' comp.id %}">
{{comp.identifier}}
</a>
<br>
{% empty %}
{% trans 'None' %}
{% endfor %}
</td>
</tr>
<tr>
<th scope="row">{% trans 'Deductions of eco-accounts' %}</th>
<td class="align-middle">
{% for deduction in deductions %}
<a href="{% url 'compensation:acc-report' deduction.account.id %}">
{{deduction.account.identifier}}
</a>
<br>
{% endfor %}
</td>
</tr>
<tr>
<th scope="row">{% trans 'Payments' %}</th>
<td class="align-middle">
{% if obj.payments.all %}
{% trans 'Exist' %}
{% else %}
{% trans 'None' %}
{% endif %}
</td>
</tr>
<tr>
<th scope="row">{% trans 'Registration date' %}</th>
<td class="align-middle">{{obj.legal.registration_date|default_if_none:""}}</td>
</tr>
<tr>
<th scope="row">{% trans 'Binding on' %}</th>
<td class="align-middle">{{obj.legal.binding_date|default_if_none:""}}</td>
</tr>
<tr>
<th scope="row">{% trans 'Last modified' %}</th>
<td class="align-middle">
{{obj.created.timestamp|default_if_none:""|naturalday}}
</td>
</tr>
</table>
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="row">
{% include 'map/geom_form.html' %}
</div>
<div class="row">
<div class="col-sm-6 col-md-6 col-lg-6">
<h4>{% trans 'Open in browser' %}</h4>
{{ qrcode|safe }}
</div>
<div class="col-sm-6 col-md-6 col-lg-6">
<h4>{% trans 'View in LANIS' %}</h4>
{{ qrcode_lanis|safe }}
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -7,16 +7,16 @@ Created on: 30.11.20
""" """
from django.urls import path from django.urls import path
from intervention.views import index_view, new_view, open_view, edit_view, remove_view, new_document_view, share_view, \ from intervention.views import index_view, new_view, detail_view, edit_view, remove_view, new_document_view, share_view, \
create_share_view, remove_revocation_view, new_revocation_view, run_check_view, log_view, new_deduction_view, \ create_share_view, remove_revocation_view, new_revocation_view, run_check_view, log_view, new_deduction_view, \
record_view, remove_document_view, get_document_view, get_revocation_view, new_id_view record_view, remove_document_view, get_document_view, get_revocation_view, new_id_view, report_view
app_name = "intervention" app_name = "intervention"
urlpatterns = [ urlpatterns = [
path("", index_view, name="index"), path("", index_view, name="index"),
path('new/', new_view, name='new'), path('new/', new_view, name='new'),
path('new/id', new_id_view, name='new-id'), path('new/id', new_id_view, name='new-id'),
path('<id>', open_view, name='open'), path('<id>', detail_view, name='detail'),
path('<id>/log', log_view, name='log'), path('<id>/log', log_view, name='log'),
path('<id>/edit', edit_view, name='edit'), path('<id>/edit', edit_view, name='edit'),
path('<id>/remove', remove_view, name='remove'), path('<id>/remove', remove_view, name='remove'),
@ -24,6 +24,7 @@ urlpatterns = [
path('<id>/share', create_share_view, name='share-create'), path('<id>/share', create_share_view, name='share-create'),
path('<id>/check', run_check_view, name='run-check'), path('<id>/check', run_check_view, name='run-check'),
path('<id>/record', record_view, name='record'), path('<id>/record', record_view, name='record'),
path('<id>/report', report_view, name='report'),
# Documents # Documents
path('<id>/document/new/', new_document_view, name='new-doc'), path('<id>/document/new/', new_document_view, name='new-doc'),

View File

@ -1,7 +1,7 @@
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.http import HttpRequest, JsonResponse from django.http import HttpRequest, JsonResponse
from django.shortcuts import render, get_object_or_404 from django.shortcuts import render
from intervention.forms.forms import NewInterventionForm, EditInterventionForm from intervention.forms.forms import NewInterventionForm, EditInterventionForm
from intervention.forms.modalForms import ShareInterventionModalForm, NewRevocationModalForm, \ from intervention.forms.modalForms import ShareInterventionModalForm, NewRevocationModalForm, \
@ -13,6 +13,7 @@ from konova.decorators import *
from konova.forms import SimpleGeomForm, NewDocumentForm, RemoveModalForm, RecordModalForm from konova.forms import SimpleGeomForm, NewDocumentForm, RemoveModalForm, RecordModalForm
from konova.sub_settings.django_settings import DEFAULT_DATE_FORMAT from konova.sub_settings.django_settings import DEFAULT_DATE_FORMAT
from konova.utils.documents import remove_document, get_document from konova.utils.documents import remove_document, get_document
from konova.utils.generators import generate_qr_code
from konova.utils.message_templates import INTERVENTION_INVALID, FORM_INVALID, IDENTIFIER_REPLACED from konova.utils.message_templates import INTERVENTION_INVALID, FORM_INVALID, IDENTIFIER_REPLACED
from konova.utils.user_checks import in_group from konova.utils.user_checks import in_group
@ -34,6 +35,8 @@ def index_view(request: HttpRequest):
# Filtering by user access is performed in table filter inside of InterventionTableFilter class # Filtering by user access is performed in table filter inside of InterventionTableFilter class
interventions = Intervention.objects.filter( interventions = Intervention.objects.filter(
deleted=None, # not deleted deleted=None, # not deleted
).select_related(
"legal"
) )
table = InterventionTable( table = InterventionTable(
request=request, request=request,
@ -74,7 +77,7 @@ def new_view(request: HttpRequest):
) )
) )
messages.success(request, _("Intervention {} added").format(intervention.identifier)) messages.success(request, _("Intervention {} added").format(intervention.identifier))
return redirect("intervention:open", id=intervention.id) return redirect("intervention:detail", id=intervention.id)
else: else:
messages.error(request, FORM_INVALID) messages.error(request, FORM_INVALID)
else: else:
@ -180,7 +183,7 @@ def remove_document_view(request: HttpRequest, doc_id: str):
@login_required @login_required
@any_group_check @any_group_check
def open_view(request: HttpRequest, id: str): def detail_view(request: HttpRequest, id: str):
""" Renders a detail view for viewing an intervention's data """ Renders a detail view for viewing an intervention's data
Args: Args:
@ -193,7 +196,14 @@ def open_view(request: HttpRequest, id: str):
template = "intervention/detail/view.html" template = "intervention/detail/view.html"
# Fetch data, filter out deleted related data # Fetch data, filter out deleted related data
intervention = get_object_or_404(Intervention, id=id) intervention = get_object_or_404(
Intervention.objects.select_related(
"geometry",
"legal",
"responsible",
),
id=id
)
compensations = intervention.compensations.filter( compensations = intervention.compensations.filter(
deleted=None, deleted=None,
) )
@ -213,7 +223,7 @@ def open_view(request: HttpRequest, id: str):
) )
context = { context = {
"intervention": intervention, "obj": intervention,
"compensations": compensations, "compensations": compensations,
"has_access": is_data_shared, "has_access": is_data_shared,
"geom_form": geom_form, "geom_form": geom_form,
@ -252,7 +262,7 @@ def edit_view(request: HttpRequest, id: str):
# The data form takes the geom form for processing, as well as the performing user # The data form takes the geom form for processing, as well as the performing user
intervention = data_form.save(request.user, geom_form) intervention = data_form.save(request.user, geom_form)
messages.success(request, _("Intervention {} edited").format(intervention.identifier)) messages.success(request, _("Intervention {} edited").format(intervention.identifier))
return redirect("intervention:open", id=intervention.id) return redirect("intervention:detail", id=intervention.id)
else: else:
messages.error(request, FORM_INVALID) messages.error(request, FORM_INVALID)
else: else:
@ -338,7 +348,7 @@ def share_view(request: HttpRequest, id: str, token: str):
_("{} has been shared with you").format(intervention.identifier) _("{} has been shared with you").format(intervention.identifier)
) )
intervention.users.add(user) intervention.users.add(user)
return redirect("intervention:open", id=id) return redirect("intervention:detail", id=id)
else: else:
messages.error( messages.error(
request, request,
@ -471,3 +481,48 @@ def record_view(request: HttpRequest, id: str):
msg_succ, msg_succ,
msg_error=_("There are errors on this intervention:") msg_error=_("There are errors on this intervention:")
) )
def report_view(request:HttpRequest, id: str):
""" Renders the public report view
Args:
request (HttpRequest): The incoming request
id (str): The id of the intervention
Returns:
"""
template = "intervention/report/report.html"
intervention = get_object_or_404(Intervention, id=id)
# If intervention is not recorded (yet or currently) we need to render another template without any data
if not intervention.recorded:
template = "report/unavailable.html"
return render(request, template, {})
# Prepare data for map viewer
geom_form = SimpleGeomForm(
instance=intervention
)
distinct_deductions = intervention.deductions.all().distinct(
"account"
)
qrcode_img = generate_qr_code(
request.build_absolute_uri(reverse("intervention:report", args=(id,))),
10
)
qrcode_img_lanis = generate_qr_code(
intervention.get_LANIS_link(),
7
)
context = {
"obj": intervention,
"deductions": distinct_deductions,
"qrcode": qrcode_img,
"qrcode_lanis": qrcode_img_lanis,
"geom_form": geom_form,
}
context = BaseContext(request, context).context
return render(request, template, context)

View File

@ -8,6 +8,7 @@ Created on: 16.11.20
from django.http import HttpRequest from django.http import HttpRequest
from konova.sub_settings.context_settings import BASE_TITLE, HELP_LINK, BASE_FRONTEND_TITLE from konova.sub_settings.context_settings import BASE_TITLE, HELP_LINK, BASE_FRONTEND_TITLE
from konova.sub_settings.django_settings import LANGUAGE_CODE
class BaseContext: class BaseContext:
@ -17,7 +18,7 @@ class BaseContext:
context = { context = {
"base_title": BASE_TITLE, "base_title": BASE_TITLE,
"base_frontend_title": BASE_FRONTEND_TITLE, "base_frontend_title": BASE_FRONTEND_TITLE,
"language": "en", "language": LANGUAGE_CODE,
"user": None, "user": None,
"current_role": None, "current_role": None,
"help_link": HELP_LINK, "help_link": HELP_LINK,

View File

@ -149,7 +149,7 @@ class BaseObject(BaseResource):
""" """
if hasattr(self, "users"): if hasattr(self, "users"):
return self.users.filter(username=user.username).exists() return self.users.filter(id=user.id)
else: else:
return User.objects.none() return User.objects.none()

View File

@ -69,3 +69,14 @@ ETS_GROUP = "Conservation office"
# Needed to redirect to LANIS # Needed to redirect to LANIS
## Values to be inserted are [zoom_level, x_coord, y_coord] ## Values to be inserted are [zoom_level, x_coord, y_coord]
LANIS_LINK_TEMPLATE = "https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/index.php?lang=de&zl={}&x={}&y={}&bl=tk_rlp_tms_grau&bo=1&lo=0.8,0.8,0.8,0.6,0.8,0.8,0.8,0.8,0.8&layers=eiv_f,eiv_l,eiv_p,kom_f,kom_l,kom_p,oek_f,ema_f,mae&service=kartendienste_naturschutz" LANIS_LINK_TEMPLATE = "https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/index.php?lang=de&zl={}&x={}&y={}&bl=tk_rlp_tms_grau&bo=1&lo=0.8,0.8,0.8,0.6,0.8,0.8,0.8,0.8,0.8&layers=eiv_f,eiv_l,eiv_p,kom_f,kom_l,kom_p,oek_f,ema_f,mae&service=kartendienste_naturschutz"
## This look up table (LUT) defines different zoom levels on the size of the calculate area of a geometry.
LANIS_ZOOM_LUT = {
1000000000: 6,
100000000: 10,
10000000: 17,
1000000: 20,
100000: 25,
10000: 28,
1000: 30,
500: 31,
}

View File

@ -10,7 +10,7 @@ For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.1/ref/settings/ https://docs.djangoproject.com/en/3.1/ref/settings/
""" """
import os import os
from pathlib import Path from django.utils.translation import gettext_lazy as _
# Build paths inside the project like this: BASE_DIR / 'subdir'. # Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = os.path.dirname( BASE_DIR = os.path.dirname(
@ -78,12 +78,12 @@ if DEBUG:
MIDDLEWARE = [ MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware', 'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
"django.middleware.locale.LocaleMiddleware",
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
"django.middleware.locale.LocaleMiddleware",
] ]
if DEBUG: if DEBUG:
MIDDLEWARE += [ MIDDLEWARE += [
@ -149,6 +149,10 @@ AUTH_PASSWORD_VALIDATORS = [
# https://docs.djangoproject.com/en/3.1/topics/i18n/ # https://docs.djangoproject.com/en/3.1/topics/i18n/
LANGUAGE_CODE = 'de' LANGUAGE_CODE = 'de'
LANGUAGES = [
('de', _('German')),
('en', _('English')),
]
USE_THOUSAND_SEPARATOR = True USE_THOUSAND_SEPARATOR = True

View File

@ -1,5 +1,10 @@
{% load i18n fontawesome_5 %} {% load i18n fontawesome_5 %}
{% comment %}
Used in e.g. reports and detail views for every model which supports comment field (BaseObject derived)
{% endcomment %}
{% if obj.comment %} {% if obj.comment %}
<div class="w-100"> <div class="w-100">
<div class="card mt-3"> <div class="card mt-3">

View File

@ -7,6 +7,10 @@ Created on: 09.11.20
""" """
import random import random
import string import string
import qrcode
import qrcode.image.svg
from io import BytesIO
def generate_random_string(length: int, use_numbers: bool = False, use_letters_lc: bool = False, use_letters_uc: bool = False) -> str: def generate_random_string(length: int, use_numbers: bool = False, use_letters_lc: bool = False, use_letters_uc: bool = False) -> str:
@ -24,3 +28,24 @@ def generate_random_string(length: int, use_numbers: bool = False, use_letters_l
elements = "".join(elements) elements = "".join(elements)
ret_val = "".join(random.choice(elements) for i in range(length)) ret_val = "".join(random.choice(elements) for i in range(length))
return ret_val return ret_val
def generate_qr_code(content: str, size: int = 20) -> str:
""" Generates a qr code from given content
Args:
content (str): The content for the qr code
size (int): The image size
Returns:
qrcode_svg (str): The qr code as svg
"""
qrcode_factory = qrcode.image.svg.SvgImage
qrcode_img = qrcode.make(
content,
image_factory=qrcode_factory,
box_size=size
)
stream = BytesIO()
qrcode_img.save(stream)
return stream.getvalue().decode()

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -20,7 +20,7 @@
<body> <body>
<header> <header>
{% block header %} {% block header %}
{% include 'navbar.html' %} {% include 'navbars/navbar.html' %}
{% endblock %} {% endblock %}
</header> </header>
<div class="container-fluid mt-3 px-5"> <div class="container-fluid mt-3 px-5">

View File

@ -0,0 +1,8 @@
<nav class="navbar navbar-expand-lg navbar-dark">
<a href="{% url 'home' %}">
<div class="nav-icon badge">
<strong>KSP</strong>
</div>
</a>
</nav>

View File

@ -0,0 +1,44 @@
<!DOCTYPE html>
{% load static i18n l10n fontawesome_5 bootstrap4 %}
<html lang="{{ language }}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ base_frontend_title }}</title>
<link rel="icon" type="image/ico" href="{% static 'images/ksp-favicon.ico' %}">
{% bootstrap_css %}
{% bootstrap_javascript jquery='full' %}
{% fontawesome_5_static %}
<link rel="stylesheet" href="{% static 'css/konova.css' %}">
{% block head %}
{% endblock %}
</head>
<body>
<header>
{% block header %}
{% include 'navbars/public_navbar.html' %}
{% endblock %}
</header>
<div class="container-fluid mt-3 px-5">
<div class="">
{% for message in messages %}
<div class="row alert alert-{{ message.tags }}">
{{ message }}
</div>
{% endfor %}
</div>
{% block body %}
{% endblock %}
</div>
{% block footer %}
{% endblock %}
</body>
</html>

View File

@ -0,0 +1,14 @@
{% extends 'public_base.html' %}
{% load i18n %}
{% block body %}
<div class="jumbotron">
<h1 class="display-4">{% trans 'Unrecorded data' %}</h1>
<hr>
<p class="lead">
{% blocktrans %}
The data you want to see is not recorded and might still be work in progress. Please come back another time.
{% endblocktrans %}
</p>
</div>
{% endblock %}