Merge pull request '26_Annual_conservation_reports' (#33) from 26_Annual_conservation_reports into master
Reviewed-on: SGD-Nord/konova#33
This commit is contained in:
commit
d47e840dbd
0
analysis/__init__.py
Normal file
0
analysis/__init__.py
Normal file
3
analysis/admin.py
Normal file
3
analysis/admin.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
5
analysis/apps.py
Normal file
5
analysis/apps.py
Normal file
@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AnalysisConfig(AppConfig):
|
||||
name = 'analysis'
|
82
analysis/forms.py
Normal file
82
analysis/forms.py
Normal file
@ -0,0 +1,82 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||
Created on: 20.10.21
|
||||
|
||||
"""
|
||||
from dal import autocomplete
|
||||
from django import forms
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from codelist.models import KonovaCode
|
||||
from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID
|
||||
from konova.forms import BaseForm
|
||||
|
||||
|
||||
class TimespanReportForm(BaseForm):
|
||||
""" TimespanReporForm is used for allowing simple creation of an e.g. annual report for conservation offices
|
||||
|
||||
"""
|
||||
date_from = forms.DateField(
|
||||
label_suffix="",
|
||||
label=_("From"),
|
||||
widget=forms.DateInput(
|
||||
attrs={
|
||||
"type": "date",
|
||||
"data-provide": "datepicker",
|
||||
"class": "form-control",
|
||||
},
|
||||
format="%d.%m.%Y"
|
||||
)
|
||||
)
|
||||
date_to = forms.DateField(
|
||||
label_suffix="",
|
||||
label=_("To"),
|
||||
widget=forms.DateInput(
|
||||
attrs={
|
||||
"type": "date",
|
||||
"data-provide": "datepicker",
|
||||
"class": "form-control",
|
||||
},
|
||||
format="%d.%m.%Y"
|
||||
)
|
||||
)
|
||||
conservation_office = forms.ModelChoiceField(
|
||||
label=_("Conservation office"),
|
||||
label_suffix="",
|
||||
help_text=_("Select the responsible office"),
|
||||
queryset=KonovaCode.objects.filter(
|
||||
is_archived=False,
|
||||
is_leaf=True,
|
||||
code_lists__in=[CODELIST_CONSERVATION_OFFICE_ID],
|
||||
),
|
||||
widget=autocomplete.ModelSelect2(
|
||||
url="codes-conservation-office-autocomplete",
|
||||
attrs={
|
||||
"data-placeholder": _("Click for selection")
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.form_title = _("Generate report")
|
||||
self.form_caption = _("Select a timespan and the desired conservation office")
|
||||
self.action_url = reverse("analysis:reports")
|
||||
self.show_cancel_btn = False
|
||||
self.action_btn_label = _("Continue")
|
||||
|
||||
def save(self) -> str:
|
||||
""" Generates a redirect url for the detail report
|
||||
|
||||
Returns:
|
||||
detail_report_url (str): The constructed detail report url
|
||||
|
||||
"""
|
||||
date_from = self.cleaned_data.get("date_from", None)
|
||||
date_to = self.cleaned_data.get("date_to", None)
|
||||
office = self.cleaned_data.get("conservation_office", None)
|
||||
detail_report_url = reverse("analysis:report-detail", args=(office.id,)) + f"?df={date_from}&dt={date_to}"
|
||||
return detail_report_url
|
3
analysis/models.py
Normal file
3
analysis/models.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
12
analysis/settings.py
Normal file
12
analysis/settings.py
Normal file
@ -0,0 +1,12 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||
Created on: 19.10.21
|
||||
|
||||
"""
|
||||
|
||||
# Defines the date of the legal publishing of the LKompVzVo
|
||||
from django.utils import timezone
|
||||
|
||||
LKOMPVZVO_PUBLISH_DATE = timezone.make_aware(timezone.datetime.fromisoformat("2018-06-16"))
|
36
analysis/templates/analysis/reports/detail.html
Normal file
36
analysis/templates/analysis/reports/detail.html
Normal file
@ -0,0 +1,36 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n fontawesome_5 %}
|
||||
|
||||
{% block body %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12 col-lg-6">
|
||||
<h3>{% trans 'Evaluation report' %} {{office.long_name}}</h3>
|
||||
<h5>{% trans 'From' %} {{report.date_from.date}} {% trans 'to' %} {{report.date_to.date}}</h5>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12 col-lg-6">
|
||||
<div class="d-flex justify-content-end">
|
||||
<div class="dropdown">
|
||||
<div class="btn btn" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
|
||||
<button class="btn btn-default" title="{% trans 'Download' %}">
|
||||
{% fa5_icon 'download' %}
|
||||
</button>
|
||||
</div>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a href="{{request.url}}?format=excel&{{request.GET.urlencode}}">
|
||||
<button class="dropdown-item" title="Excel">
|
||||
{% fa5_icon 'file-excel' %} Excel
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="col-sm-12 col-md-12 col-lg-12">
|
||||
{% include 'analysis/reports/includes/intervention/card_intervention.html' %}
|
||||
{% include 'analysis/reports/includes/compensation/card_compensation.html' %}
|
||||
{% include 'analysis/reports/includes/eco_account/card_eco_account.html' %}
|
||||
{% include 'analysis/reports/includes/old_data/card_old_interventions.html' %}
|
||||
</div>
|
||||
{% endblock %}
|
@ -0,0 +1,55 @@
|
||||
{% load i18n fontawesome_5 ksp_filters %}
|
||||
|
||||
<h3>{% trans 'Amount' %}</h3>
|
||||
<strong>
|
||||
{% blocktrans %}
|
||||
Checked = Has been checked by the registration office according to LKompVzVo
|
||||
{% endblocktrans %}
|
||||
<br>
|
||||
{% blocktrans %}
|
||||
Recorded = Has been checked and published by the conservation office
|
||||
{% endblocktrans %}
|
||||
</strong>
|
||||
<div class="table-container">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{% trans 'Area of responsibility' %}</th>
|
||||
<th scope="col">{% fa5_icon 'star' %} {% trans 'Checked' %}</th>
|
||||
<th scope="col">{% fa5_icon 'bookmark' %} {% trans 'Recorded' %}</th>
|
||||
<th scope="col">{% trans 'Number single areas' %}</th>
|
||||
<th scope="col">{% trans 'Total' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{% trans 'Conservation office by law' %}</td>
|
||||
<td>{{report.compensation_report.queryset_registration_office_unb_checked_count|default_if_zero:"-"}}</td>
|
||||
<td>{{report.compensation_report.queryset_registration_office_unb_recorded_count|default_if_zero:"-"}}</td>
|
||||
<td>{{report.compensation_report.num_single_surfaces_total_unb|default_if_zero:"-"}}</td>
|
||||
<td>{{report.compensation_report.queryset_registration_office_unb_count|default_if_zero:"-"}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Land-use planning' %}</td>
|
||||
<td>{{report.compensation_report.queryset_registration_office_tbp_checked_count|default_if_zero:"-"}}</td>
|
||||
<td>{{report.compensation_report.queryset_registration_office_tbp_recorded_count|default_if_zero:"-"}}</td>
|
||||
<td>{{report.compensation_report.num_single_surfaces_total_tbp|default_if_zero:"-"}}</td>
|
||||
<td>{{report.compensation_report.queryset_registration_office_tbp_count|default_if_zero:"-"}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Other registration office' %}</td>
|
||||
<td>{{report.compensation_report.queryset_registration_office_other_checked_count|default_if_zero:"-"}}</td>
|
||||
<td>{{report.compensation_report.queryset_registration_office_other_recorded_count|default_if_zero:"-"}}</td>
|
||||
<td>{{report.compensation_report.num_single_surfaces_total_other|default_if_zero:"-"}}</td>
|
||||
<td>{{report.compensation_report.queryset_registration_office_other_count|default_if_zero:"-"}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>{% trans 'Total' %}</strong></td>
|
||||
<td><strong>{{report.compensation_report.queryset_checked_count|default_if_zero:"-"}}</strong></td>
|
||||
<td><strong>{{report.compensation_report.queryset_recorded_count|default_if_zero:"-"}}</strong></td>
|
||||
<td><strong>{{report.compensation_report.num_single_surfaces_total|default_if_zero:"-"}}</strong></td>
|
||||
<td><strong>{{report.compensation_report.queryset_count|default_if_zero:"-"}}</strong></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
@ -0,0 +1,23 @@
|
||||
{% load i18n fontawesome_5 %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12 col-lg-12">
|
||||
<div class="card">
|
||||
<div id="compensation" class="card-header cursor-pointer rlp-r" data-toggle="collapse" data-target="#compensationBody" aria-expanded="true" aria-controls="compensationBody">
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<h5>
|
||||
{% fa5_icon 'leaf' %}
|
||||
{% trans 'Compensations' %}
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="compensationBody" class="collapse" aria-labelledby="compensation">
|
||||
<div class="card-body">
|
||||
{% include 'analysis/reports/includes/compensation/amount.html' %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,24 @@
|
||||
{% load i18n fontawesome_5 ksp_filters %}
|
||||
|
||||
<h3>{% trans 'Amount' %}</h3>
|
||||
<strong>
|
||||
{% blocktrans %}
|
||||
Recorded = Has been checked and published by the conservation office
|
||||
{% endblocktrans %}
|
||||
</strong>
|
||||
<div class="table-container">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="w-25">{% fa5_icon 'bookmark' %} {% trans 'Recorded' %}</th>
|
||||
<th scope="col">{% trans 'Total' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{report.eco_account_report.queryset_recorded_count|default_if_zero:"-"}}</td>
|
||||
<td>{{report.eco_account_report.queryset_count|default_if_zero:"-"}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
@ -0,0 +1,25 @@
|
||||
{% load i18n fontawesome_5 %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12 col-lg-12">
|
||||
<div class="card">
|
||||
<div id="ecoAccounts" class="card-header cursor-pointer rlp-r" data-toggle="collapse" data-target="#ecoAccountsBody" aria-expanded="true" aria-controls="ecoAccountsBody">
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<h5>
|
||||
{% fa5_icon 'tree' %}
|
||||
{% trans 'Eco-Accounts' %}
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="ecoAccountsBody" class="collapse" aria-labelledby="ecoAccounts">
|
||||
<div class="card-body">
|
||||
{% include 'analysis/reports/includes/eco_account/amount.html' %}
|
||||
<hr>
|
||||
{% include 'analysis/reports/includes/eco_account/deductions.html' %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,23 @@
|
||||
{% load i18n fontawesome_5 ksp_filters %}
|
||||
|
||||
<h3>{% trans 'Deductions' %}</h3>
|
||||
<div class="table-container">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{% fa5_icon 'bookmark' %} {% trans 'Recorded' %}</th>
|
||||
<th scope="col">{% fa5_icon 'bookmark' %} {% trans 'Recorded' %} {% trans 'Surface' %}</th>
|
||||
<th scope="col" class="w-25">{% trans 'Total' %}</th>
|
||||
<th scope="col" class="w-25">{% trans 'Total' %} {% trans 'Surface' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{report.eco_account_report.queryset_deductions_recorded_count|default_if_zero:"-"}}</td>
|
||||
<td>{{report.eco_account_report.recorded_deductions_sq_m|default_if_zero:"-"}}m²</td>
|
||||
<td>{{report.eco_account_report.queryset_deductions_count|default_if_zero:"-"}}</td>
|
||||
<td>{{report.eco_account_report.deductions_sq_m|default_if_zero:"-"}}m²</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
@ -0,0 +1,30 @@
|
||||
{% load i18n fontawesome_5 ksp_filters %}
|
||||
|
||||
<h3>{% trans 'Amount' %}</h3>
|
||||
<strong>
|
||||
{% blocktrans %}
|
||||
Checked = Has been checked by the registration office according to LKompVzVo
|
||||
{% endblocktrans %}
|
||||
<br>
|
||||
{% blocktrans %}
|
||||
Recorded = Has been checked and published by the conservation office
|
||||
{% endblocktrans %}
|
||||
</strong>
|
||||
<div class="table-container">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{% fa5_icon 'star' %} {% trans 'Checked' %}</th>
|
||||
<th scope="col">{% fa5_icon 'bookmark' %} {% trans 'Recorded' %}</th>
|
||||
<th scope="col" class="w-25">{% trans 'Total' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{report.intervention_report.queryset_checked_count|default_if_zero:"-"}}</td>
|
||||
<td>{{report.intervention_report.queryset_recorded_count|default_if_zero:"-"}}</td>
|
||||
<td>{{report.intervention_report.queryset_count|default_if_zero:"-"}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
@ -0,0 +1,26 @@
|
||||
{% load i18n fontawesome_5 %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12 col-lg-12">
|
||||
<div class="card">
|
||||
<div id="intervention" class="card-header cursor-pointer rlp-r" data-toggle="collapse" data-target="#interventionBody" aria-expanded="true" aria-controls="interventionBody">
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<h5>
|
||||
{% fa5_icon 'pencil-ruler' %}
|
||||
{% trans 'Interventions' %}
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="interventionBody" class="collapse" aria-labelledby="intervention">
|
||||
<div class="card-body">
|
||||
{% include 'analysis/reports/includes/intervention/amount.html' %}
|
||||
<hr>
|
||||
{% include 'analysis/reports/includes/intervention/compensated_by.html' %}
|
||||
<hr>
|
||||
{% include 'analysis/reports/includes/intervention/laws.html' %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,34 @@
|
||||
{% load i18n fontawesome_5 ksp_filters %}
|
||||
<h3>{% trans 'Compensated by' %}</h3>
|
||||
<div class="table-container scroll-300">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="w-25" scope="col">{% trans 'Compensation type' %}</th>
|
||||
<th class="w-25" scope="col">{% fa5_icon 'star' %} {% trans 'Checked' %}</th>
|
||||
<th class="w-25" scope="col">{% fa5_icon 'bookmark' %} {% trans 'Recorded' %}</th>
|
||||
<th class="w-25" scope="col">{% trans 'Total' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>{% trans 'Compensation' %}</th>
|
||||
<td>{{report.intervention_report.compensation_sum_checked|default_if_zero:"-"}}</td>
|
||||
<td>{{report.intervention_report.compensation_sum_recorded|default_if_zero:"-"}}</td>
|
||||
<td>{{report.intervention_report.compensation_sum|default_if_zero:"-"}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{% trans 'Payment' %}</th>
|
||||
<td>{{report.intervention_report.payment_sum_checked|default_if_zero:"-"}}</td>
|
||||
<td>{{report.intervention_report.payment_sum_recorded|default_if_zero:"-"}}</td>
|
||||
<td>{{report.intervention_report.payment_sum|default_if_zero:"-"}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{% trans 'Deductions' %}</th>
|
||||
<td>{{report.intervention_report.deduction_sum_checked|default_if_zero:"-"}}</td>
|
||||
<td>{{report.intervention_report.deduction_sum_recorded|default_if_zero:"-"}}</td>
|
||||
<td>{{report.intervention_report.deduction_sum|default_if_zero:"-"}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
@ -0,0 +1,50 @@
|
||||
{% load i18n fontawesome_5 ksp_filters %}
|
||||
<h3>{% trans 'Law usage' %}</h3>
|
||||
<strong>
|
||||
{% blocktrans %}
|
||||
Please note: One intervention can be based on multiple laws. This table therefore does not
|
||||
count
|
||||
{% endblocktrans %}
|
||||
</strong>
|
||||
<div class="table-container scroll-300">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="w-25" scope="col">
|
||||
{% trans 'Law' %}
|
||||
</th>
|
||||
<th scope="col">
|
||||
{% fa5_icon 'star' %} {% trans 'Checked' %}
|
||||
</th>
|
||||
<th scope="col">
|
||||
{% fa5_icon 'bookmark' %} {% trans 'Recorded' %}
|
||||
</th>
|
||||
<th scope="col">
|
||||
{% trans 'Total' %}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for law in report.intervention_report.evaluated_laws %}
|
||||
<tr>
|
||||
<td>
|
||||
{{law.short_name}}
|
||||
<br>
|
||||
<small>
|
||||
{{law.long_name}}
|
||||
</small>
|
||||
</td>
|
||||
<td>{{law.num_checked|default_if_zero:"-"}}</td>
|
||||
<td>{{law.num_recorded|default_if_zero:"-"}}</td>
|
||||
<td>{{law.num|default_if_zero:"-"}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td><strong>{% trans 'Total' %}</strong></td>
|
||||
<td><strong>{{report.intervention_report.law_sum_checked|default_if_zero:"-"}}</strong></td>
|
||||
<td><strong>{{report.intervention_report.law_sum_recorded|default_if_zero:"-"}}</strong></td>
|
||||
<td><strong>{{report.intervention_report.law_sum|default_if_zero:"-"}}</strong></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
@ -0,0 +1,40 @@
|
||||
{% load i18n fontawesome_5 ksp_filters %}
|
||||
|
||||
<h3>{% trans 'Amount' %}</h3>
|
||||
<strong>
|
||||
{% blocktrans %}
|
||||
Checked = Has been checked by the registration office according to LKompVzVo
|
||||
{% endblocktrans %}
|
||||
<br>
|
||||
{% blocktrans %}
|
||||
Recorded = Has been checked and published by the conservation office
|
||||
{% endblocktrans %}
|
||||
</strong>
|
||||
<div class="table-container">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="w-25">{% fa5_icon 'star' %} {% trans 'Type' %}</th>
|
||||
<th scope="col">{% fa5_icon 'bookmark' %} {% trans 'Recorded' %}</th>
|
||||
<th scope="col">{% trans 'Total' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{% trans 'Intervention' %}</td>
|
||||
<td>{{report.old_data_report.queryset_intervention_recorded_count|default_if_zero:"-"}}</td>
|
||||
<td>{{report.old_data_report.queryset_intervention_count|default_if_zero:"-"}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Compensation' %}</td>
|
||||
<td>{{report.old_data_report.queryset_comps_recorded_count|default_if_zero:"-"}}</td>
|
||||
<td>{{report.old_data_report.queryset_comps_count|default_if_zero:"-"}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Eco-account' %}</td>
|
||||
<td>{{report.old_data_report.queryset_acc_recorded_count|default_if_zero:"-"}}</td>
|
||||
<td>{{report.old_data_report.queryset_acc_count|default_if_zero:"-"}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
@ -0,0 +1,24 @@
|
||||
{% load i18n fontawesome_5 %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12 col-lg-12">
|
||||
<div class="card">
|
||||
<div id="oldIntervention" class="card-header cursor-pointer rlp-r" data-toggle="collapse" data-target="#oldInterventionBody" aria-expanded="true" aria-controls="oldInterventionBody">
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<h5>
|
||||
{% fa5_icon 'pencil-ruler' %}
|
||||
{% trans 'Old interventions' %}
|
||||
</h5>
|
||||
<span>{% trans 'Before' %} 16.06.2018</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="oldInterventionBody" class="collapse" aria-labelledby="oldIntervention">
|
||||
<div class="card-body">
|
||||
{% include 'analysis/reports/includes/old_data/amount.html' %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
10
analysis/templates/analysis/reports/index.html
Normal file
10
analysis/templates/analysis/reports/index.html
Normal file
@ -0,0 +1,10 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n fontawesome_5 %}
|
||||
|
||||
{% block body %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12 col-lg-12">
|
||||
{% include 'form/table/generic_table_form.html' %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
3
analysis/tests.py
Normal file
3
analysis/tests.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
15
analysis/urls.py
Normal file
15
analysis/urls.py
Normal file
@ -0,0 +1,15 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||
Created on: 15.10.21
|
||||
|
||||
"""
|
||||
from django.urls import path
|
||||
from analysis.views import *
|
||||
|
||||
app_name = "analysis"
|
||||
urlpatterns = [
|
||||
path("reports/", index_reports_view, name="reports"),
|
||||
path("reports/<id>", detail_report_view, name="report-detail"),
|
||||
]
|
114
analysis/utils/excel/excel.py
Normal file
114
analysis/utils/excel/excel.py
Normal file
@ -0,0 +1,114 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||
Created on: 21.10.21
|
||||
|
||||
"""
|
||||
from django.core.files.temp import NamedTemporaryFile
|
||||
from openpyxl import load_workbook
|
||||
|
||||
|
||||
class TempExcelFile:
|
||||
""" A temporary excel sheet which will not be saved on the hard drive permanently.
|
||||
|
||||
Using a template file and a template_map dictionary, this class can be used to fill in automatically
|
||||
predefined values into certain cells.
|
||||
|
||||
Can be used to create excel files from data and sending it as a response like
|
||||
_file = TempExcelFile()
|
||||
response = HttpResponse(
|
||||
content=file.stream,
|
||||
content_type="application/ms-excel",
|
||||
)
|
||||
response['Content-Disposition'] = 'attachment; filename=my_file.xlsx'
|
||||
return response
|
||||
|
||||
"""
|
||||
stream = None
|
||||
_template_file_path = None
|
||||
_template_map = {}
|
||||
_data_obj = None
|
||||
|
||||
def __init__(self, template_file_path: str = None, template_map: dict = None):
|
||||
self._template_map = template_map or {}
|
||||
self._template_file_path = template_file_path
|
||||
self._workbook = load_workbook(template_file_path)
|
||||
self._file = NamedTemporaryFile()
|
||||
|
||||
self._replace_template_placeholders()
|
||||
|
||||
def _replace_template_placeholders(self, start_row: int = 0):
|
||||
""" Replaces all placeholder inside the template according to _template_map
|
||||
|
||||
Args:
|
||||
start_row (int): Defines where to start
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
sheets = self._workbook.worksheets
|
||||
for sheet in sheets:
|
||||
ws = sheet
|
||||
# Always activate sheet protection
|
||||
ws.protection.sheet = True
|
||||
ws.protection.enable()
|
||||
_rows = ws.iter_rows(start_row)
|
||||
for row in _rows:
|
||||
for cell in row:
|
||||
val = cell.value
|
||||
if val in self._template_map:
|
||||
attr = self._template_map[val]
|
||||
# If keyword '_iter' can be found inside the placeholder value it's an iterable and we
|
||||
# need to process it differently
|
||||
if isinstance(attr, dict):
|
||||
# Read the iterable object and related attributes from the dict
|
||||
_iter_obj = attr.get("iterable", None)
|
||||
_attrs = attr.get("attrs", [])
|
||||
self._add_cells_from_iterable(ws, cell, _iter_obj, _attrs)
|
||||
# Since the sheet length did change now, we need to rerun this function starting with the new
|
||||
# row counter
|
||||
self._replace_template_placeholders(start_row=cell.row + len(_iter_obj))
|
||||
else:
|
||||
cell.value = attr
|
||||
self._workbook.save(self._file.name)
|
||||
self._file.seek(0)
|
||||
self.stream = self._file.read()
|
||||
|
||||
def _add_cells_from_iterable(self, ws, start_cell, _iter_obj: iter, _attrs: list):
|
||||
"""
|
||||
Adds iterable data defined by _template_map like
|
||||
...
|
||||
"some_placeholder_iter": {
|
||||
"iterable": iterable_object,
|
||||
"attrs": [
|
||||
"attr1",
|
||||
"attr2",
|
||||
"attr3",
|
||||
...
|
||||
]
|
||||
},
|
||||
...
|
||||
|
||||
Args:
|
||||
ws (Workbook): The active workbook
|
||||
_iter_obj (dict): Iterable definitions from template_map
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
# Save border style
|
||||
border_style = start_cell.border.copy()
|
||||
# Drop current row, since it is just placeholder
|
||||
ws.delete_rows(start_cell.row)
|
||||
# Add enoug empty rows for the data
|
||||
ws.insert_rows(start_cell.row, len(_iter_obj))
|
||||
|
||||
i = 0
|
||||
for _iter_entry in _iter_obj:
|
||||
j = 0
|
||||
for _iter_attr in _attrs:
|
||||
_new_cell = ws.cell(start_cell.row + i, start_cell.column + j, getattr(_iter_entry, _iter_attr))
|
||||
_new_cell.border = border_style
|
||||
j += 1
|
||||
i += 1
|
BIN
analysis/utils/excel/excel_report.xlsx
Normal file
BIN
analysis/utils/excel/excel_report.xlsx
Normal file
Binary file not shown.
547
analysis/utils/report.py
Normal file
547
analysis/utils/report.py
Normal file
@ -0,0 +1,547 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||
Created on: 18.10.21
|
||||
|
||||
"""
|
||||
from django.contrib.gis.db.models import MultiPolygonField
|
||||
from django.contrib.gis.db.models.functions import NumGeometries
|
||||
from django.db.models import Count, Sum, Q
|
||||
from django.db.models.functions import Cast
|
||||
|
||||
from analysis.settings import LKOMPVZVO_PUBLISH_DATE
|
||||
from codelist.models import KonovaCode
|
||||
from codelist.settings import CODELIST_LAW_ID
|
||||
from compensation.models import Compensation, Payment, EcoAccountDeduction, EcoAccount
|
||||
from intervention.models import Intervention
|
||||
from konova.models import Geometry
|
||||
from konova.sub_settings.django_settings import BASE_DIR, DEFAULT_DATE_FORMAT
|
||||
|
||||
|
||||
class TimespanReport:
|
||||
""" Holds multiple report elements for a timespan report
|
||||
|
||||
"""
|
||||
office_id = -1
|
||||
date_from = -1
|
||||
date_to = -1
|
||||
|
||||
# Excel map is used to map a cell value ("A1") to an attribute
|
||||
excel_map = {}
|
||||
excel_template_path = f"{BASE_DIR}/analysis/utils/excel/excel_report.xlsx"
|
||||
|
||||
class InterventionReport:
|
||||
queryset = Intervention.objects.none()
|
||||
queryset_checked = Intervention.objects.none()
|
||||
queryset_recorded = Intervention.objects.none()
|
||||
|
||||
queryset_count = -1
|
||||
queryset_checked_count = -1
|
||||
queryset_recorded_count = -1
|
||||
|
||||
# Law related
|
||||
law_sum = -1
|
||||
law_sum_checked = -1
|
||||
law_sum_recorded = -1
|
||||
evaluated_laws = None
|
||||
|
||||
# Compensations related
|
||||
compensation_sum = -1
|
||||
compensation_sum_checked = -1
|
||||
compensation_sum_recorded = -1
|
||||
payment_sum = -1
|
||||
payment_sum_checked = -1
|
||||
payment_sum_recorded = -1
|
||||
deduction_sum = -1
|
||||
deduction_sum_checked = -1
|
||||
deduction_sum_recorded = -1
|
||||
|
||||
excel_map = {}
|
||||
|
||||
def __init__(self, id: str, date_from: str, date_to: str):
|
||||
self.queryset = Intervention.objects.filter(
|
||||
responsible__conservation_office__id=id,
|
||||
legal__registration_date__gt=LKOMPVZVO_PUBLISH_DATE,
|
||||
deleted=None,
|
||||
created__timestamp__gte=date_from,
|
||||
created__timestamp__lte=date_to,
|
||||
)
|
||||
self.queryset_checked = self.queryset.filter(
|
||||
checked__isnull=False
|
||||
)
|
||||
self.queryset_recorded = self.queryset.filter(
|
||||
recorded__isnull=False
|
||||
)
|
||||
self.queryset_count = self.queryset.count()
|
||||
self.queryset_checked_count = self.queryset_checked.count()
|
||||
self.queryset_recorded_count = self.queryset_recorded.count()
|
||||
|
||||
self._create_report()
|
||||
self._define_excel_map()
|
||||
|
||||
def _define_excel_map(self):
|
||||
""" Define the excel map, which holds values for each placeholder used in the template
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
self.excel_map = {
|
||||
"i_checked": self.queryset_checked_count,
|
||||
"i_recorded": self.queryset_recorded_count,
|
||||
"i_total": self.queryset_count,
|
||||
"i_compensations_checked": self.compensation_sum_checked,
|
||||
"i_compensations_recorded": self.compensation_sum_recorded,
|
||||
"i_compensations_total": self.compensation_sum,
|
||||
"i_payments_recorded": self.payment_sum_recorded,
|
||||
"i_payments_checked": self.payment_sum_checked,
|
||||
"i_payments_total": self.payment_sum,
|
||||
"i_deductions_recorded": self.deduction_sum_recorded,
|
||||
"i_deductions_checked": self.deduction_sum_checked,
|
||||
"i_deductions_total": self.deduction_sum,
|
||||
"i_laws_iter": {
|
||||
"iterable": self.evaluated_laws,
|
||||
"attrs": [
|
||||
"short_name",
|
||||
"num_checked",
|
||||
"num_recorded",
|
||||
"num",
|
||||
]
|
||||
},
|
||||
"i_laws_checked": self.law_sum_checked,
|
||||
"i_laws_recorded": self.law_sum_recorded,
|
||||
"i_laws_total": self.law_sum,
|
||||
}
|
||||
|
||||
def _create_report(self):
|
||||
""" Creates all report information
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
self._evaluate_laws()
|
||||
self._evaluate_compensations()
|
||||
|
||||
def _evaluate_laws(self):
|
||||
""" Analyzes the intervention-law distribution
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
# Count interventions based on law
|
||||
# Fetch all KonovaCodes for laws, sorted alphabetically
|
||||
laws = KonovaCode.objects.filter(
|
||||
is_archived=False,
|
||||
is_leaf=True,
|
||||
code_lists__in=[CODELIST_LAW_ID],
|
||||
).order_by(
|
||||
"long_name"
|
||||
)
|
||||
# Fetch all law ids which are used by any .legal object of an intervention object
|
||||
intervention_laws_total = self.queryset.values_list("legal__laws__id")
|
||||
intervention_laws_checked = self.queryset.filter(checked__isnull=False).values_list("legal__laws__id")
|
||||
intervention_laws_recorded = self.queryset.filter(recorded__isnull=False).values_list(
|
||||
"legal__laws__id")
|
||||
# Count how often which law id appears in the above list, return only the long_name of the law and the resulting
|
||||
# count (here 'num'). This is for keeping the db fetch as small as possible
|
||||
# Compute the sum for total, checked and recorded
|
||||
self.evaluated_laws = laws.annotate(
|
||||
num=Count("id", filter=Q(id__in=intervention_laws_total)),
|
||||
num_checked=Count("id", filter=Q(id__in=intervention_laws_checked)),
|
||||
num_recorded=Count("id", filter=Q(id__in=intervention_laws_recorded)),
|
||||
).values_list("short_name", "long_name", "num_checked", "num_recorded", "num", named=True)
|
||||
self.law_sum = self.evaluated_laws.aggregate(sum_num=Sum("num"))["sum_num"]
|
||||
self.law_sum_checked = self.evaluated_laws.aggregate(sum_num_checked=Sum("num_checked"))["sum_num_checked"]
|
||||
self.law_sum_recorded = self.evaluated_laws.aggregate(sum_num_recorded=Sum("num_recorded"))["sum_num_recorded"]
|
||||
|
||||
def _evaluate_compensations(self):
|
||||
""" Analyzes the types of compensation distribution
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
# Count all compensations
|
||||
comps = Compensation.objects.filter(
|
||||
intervention__in=self.queryset
|
||||
)
|
||||
self.compensation_sum = comps.count()
|
||||
self.compensation_sum_checked = comps.filter(intervention__checked__isnull=False).count()
|
||||
self.compensation_sum_recorded = comps.filter(intervention__recorded__isnull=False).count()
|
||||
|
||||
# Count all payments
|
||||
payments = Payment.objects.filter(
|
||||
intervention__in=self.queryset
|
||||
)
|
||||
self.payment_sum = payments.count()
|
||||
self.payment_sum_checked = payments.filter(intervention__checked__isnull=False).count()
|
||||
self.payment_sum_recorded = payments.filter(intervention__recorded__isnull=False).count()
|
||||
|
||||
# Count all deductions
|
||||
deductions = EcoAccountDeduction.objects.filter(
|
||||
intervention__in=self.queryset
|
||||
)
|
||||
self.deduction_sum = deductions.count()
|
||||
self.deduction_sum_checked = deductions.filter(intervention__checked__isnull=False).count()
|
||||
self.deduction_sum_recorded = deductions.filter(intervention__recorded__isnull=False).count()
|
||||
|
||||
class CompensationReport:
|
||||
queryset = Compensation.objects.none()
|
||||
queryset_checked = Compensation.objects.none()
|
||||
queryset_recorded = Compensation.objects.none()
|
||||
queryset_count = -1
|
||||
queryset_checked_count = -1
|
||||
queryset_recorded_count = -1
|
||||
|
||||
queryset_registration_office_unb = Compensation.objects.none()
|
||||
queryset_registration_office_unb_checked = Compensation.objects.none()
|
||||
queryset_registration_office_unb_recorded = Compensation.objects.none()
|
||||
queryset_registration_office_unb_count = -1
|
||||
queryset_registration_office_unb_checked_count = -1
|
||||
queryset_registration_office_unb_recorded_count = -1
|
||||
num_single_surfaces_total_unb = -1
|
||||
|
||||
queryset_registration_office_tbp = Compensation.objects.none()
|
||||
queryset_registration_office_tbp_checked = Compensation.objects.none()
|
||||
queryset_registration_office_tbp_recorded = Compensation.objects.none()
|
||||
queryset_registration_office_tbp_count = -1
|
||||
queryset_registration_office_tbp_checked_count = -1
|
||||
queryset_registration_office_tbp_recorded_count = -1
|
||||
num_single_surfaces_total_tbp = -1
|
||||
|
||||
queryset_registration_office_other = Compensation.objects.none()
|
||||
queryset_registration_office_other_checked = Compensation.objects.none()
|
||||
queryset_registration_office_other_recorded = Compensation.objects.none()
|
||||
queryset_registration_office_other_count = -1
|
||||
queryset_registration_office_other_checked_count = -1
|
||||
queryset_registration_office_other_recorded_count = -1
|
||||
num_single_surfaces_total_other = -1
|
||||
|
||||
num_single_surfaces_total = -1
|
||||
num_single_surfaces_recorded = -1
|
||||
|
||||
# Code list id for 'Träger der Bauleitplanung' parent
|
||||
id_tbp = 1943695
|
||||
# Code list id for 'untere Naturschutzbehörde'
|
||||
id_unb = 1943087
|
||||
# Code list id for 'obere Naturschutzbehörde'
|
||||
id_onb = 1943084
|
||||
|
||||
def __init__(self, id: str, date_from: str, date_to: str):
|
||||
self.queryset = Compensation.objects.filter(
|
||||
intervention__responsible__conservation_office__id=id,
|
||||
intervention__legal__registration_date__gt=LKOMPVZVO_PUBLISH_DATE,
|
||||
deleted=None,
|
||||
intervention__created__timestamp__gte=date_from,
|
||||
intervention__created__timestamp__lte=date_to,
|
||||
)
|
||||
self.queryset_checked = self.queryset.filter(
|
||||
intervention__checked__isnull=False
|
||||
)
|
||||
self.queryset_recorded = self.queryset.filter(
|
||||
intervention__recorded__isnull=False
|
||||
)
|
||||
|
||||
self.queryset_count = self.queryset.count()
|
||||
self.queryset_checked_count = self.queryset_checked.count()
|
||||
self.queryset_recorded_count = self.queryset_recorded.count()
|
||||
|
||||
self._create_report()
|
||||
self._define_excel_map()
|
||||
|
||||
def _define_excel_map(self):
|
||||
""" Define the excel map, which holds values for each placeholder used in the template
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
self.excel_map = {
|
||||
"c_unb_checked": self.queryset_registration_office_unb_checked_count,
|
||||
"c_unb_recorded": self.queryset_registration_office_unb_recorded_count,
|
||||
"c_unb": self.queryset_registration_office_unb_count,
|
||||
"c_surfaces_unb": self.num_single_surfaces_total_unb,
|
||||
"c_tbp_checked": self.queryset_registration_office_tbp_checked_count,
|
||||
"c_tbp_recorded": self.queryset_registration_office_tbp_recorded_count,
|
||||
"c_tbp": self.queryset_registration_office_tbp_count,
|
||||
"c_surfaces_tbp": self.num_single_surfaces_total_tbp,
|
||||
"c_other_checked": self.queryset_registration_office_other_checked_count,
|
||||
"c_other_recorded": self.queryset_registration_office_other_recorded_count,
|
||||
"c_other": self.queryset_registration_office_other_count,
|
||||
"c_surfaces_other": self.num_single_surfaces_total_other,
|
||||
"c_checked": self.queryset_checked_count,
|
||||
"c_recorded": self.queryset_recorded_count,
|
||||
"c_total": self.queryset_count,
|
||||
"c_surfaces": self.num_single_surfaces_total,
|
||||
}
|
||||
|
||||
def _create_report(self):
|
||||
""" Creates all report information
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
self._evaluate_compensation_responsibility()
|
||||
self._evaluate_surfaces()
|
||||
|
||||
def _evaluate_surfaces(self):
|
||||
""" Evaluates the surfaces of compensation Multipolygon fields
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
# Evaluate all surfaces
|
||||
ids = self.queryset.values_list("geometry_id")
|
||||
self.num_single_surfaces_total = self._count_geometry_surfaces(ids)
|
||||
|
||||
# Evaluate surfaces where the conservation office is the registration office as well
|
||||
ids = self.queryset_registration_office_unb.values_list("geometry_id")
|
||||
self.num_single_surfaces_total_unb = self._count_geometry_surfaces(ids)
|
||||
|
||||
# Evaluates surfaces where the registration office is a Träger Bauleitplanung
|
||||
ids = self.queryset_registration_office_tbp.values_list("geometry_id")
|
||||
self.num_single_surfaces_total_tbp = self._count_geometry_surfaces(ids)
|
||||
|
||||
# Evaluates surfaces where any other registration office is responsible
|
||||
ids = self.queryset_registration_office_other.values_list("geometry_id")
|
||||
self.num_single_surfaces_total_other = self._count_geometry_surfaces(ids)
|
||||
|
||||
def _count_geometry_surfaces(self, ids: list):
|
||||
""" Wraps counting of geometry surfaces from a given list of ids
|
||||
|
||||
Args:
|
||||
ids (list): List of geometry ids
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
# Now select all geometries matching the ids
|
||||
# Then perform a ST_NumGeometries variant over all geometries
|
||||
# Then sum up all of the calculated surface numbers
|
||||
return Geometry.objects.filter(
|
||||
id__in=ids
|
||||
).annotate(
|
||||
geom_cast=Cast("geom", MultiPolygonField())
|
||||
).annotate(
|
||||
num=NumGeometries("geom_cast")
|
||||
).aggregate(
|
||||
num_geoms=Sum("num")
|
||||
)["num_geoms"] or 0
|
||||
|
||||
def _evaluate_compensation_responsibility(self):
|
||||
""" Evaluates compensations based on different responsibility areas
|
||||
|
||||
unb -> Untere Naturschutzbehörde
|
||||
Holds entries where conservation_office and registration_office basically are the same
|
||||
tbp -> Träger Bauleitplanung
|
||||
Holds entries where registration_office is a Träger der Bauleitplanung
|
||||
other -> Other registration offices
|
||||
Holds all other entries
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
self.queryset_registration_office_unb = self.queryset.filter(
|
||||
intervention__responsible__registration_office__parent__id=self.id_unb
|
||||
)
|
||||
self.queryset_registration_office_unb_recorded = self.queryset_registration_office_unb.filter(
|
||||
intervention__recorded__isnull=False,
|
||||
)
|
||||
self.queryset_registration_office_unb_checked = self.queryset_registration_office_unb.filter(
|
||||
intervention__checked__isnull=False,
|
||||
)
|
||||
self.queryset_registration_office_unb_count = self.queryset_registration_office_unb.count()
|
||||
self.queryset_registration_office_unb_checked_count = self.queryset_registration_office_unb_checked.count()
|
||||
self.queryset_registration_office_unb_recorded_count = self.queryset_registration_office_unb_recorded.count()
|
||||
|
||||
self.queryset_registration_office_tbp = self.queryset.filter(
|
||||
intervention__responsible__registration_office__parent__id=self.id_tbp
|
||||
)
|
||||
self.queryset_registration_office_tbp_recorded = self.queryset_registration_office_tbp.filter(
|
||||
intervention__recorded__isnull=False,
|
||||
)
|
||||
self.queryset_registration_office_tbp_checked = self.queryset_registration_office_tbp.filter(
|
||||
intervention__checked__isnull=False,
|
||||
)
|
||||
self.queryset_registration_office_tbp_count = self.queryset_registration_office_tbp.count()
|
||||
self.queryset_registration_office_tbp_checked_count = self.queryset_registration_office_tbp_checked.count()
|
||||
self.queryset_registration_office_tbp_recorded_count = self.queryset_registration_office_tbp_recorded.count()
|
||||
|
||||
self.queryset_registration_office_other = self.queryset.exclude(
|
||||
Q(id__in=self.queryset_registration_office_tbp) | Q(id__in=self.queryset_registration_office_unb)
|
||||
)
|
||||
self.queryset_registration_office_other_recorded = self.queryset_registration_office_other.filter(
|
||||
intervention__recorded__isnull=False,
|
||||
)
|
||||
self.queryset_registration_office_other_checked = self.queryset_registration_office_other.filter(
|
||||
intervention__checked__isnull=False,
|
||||
)
|
||||
self.queryset_registration_office_other_count = self.queryset_registration_office_other.count()
|
||||
self.queryset_registration_office_other_checked_count = self.queryset_registration_office_other_checked.count()
|
||||
self.queryset_registration_office_other_recorded_count = self.queryset_registration_office_other_recorded.count()
|
||||
|
||||
class EcoAccountReport:
|
||||
queryset = EcoAccount.objects.none()
|
||||
queryset_recorded = EcoAccount.objects.none()
|
||||
queryset_count = -1
|
||||
queryset_recorded_count = -1
|
||||
|
||||
queryset_deductions = EcoAccountDeduction.objects.none()
|
||||
queryset_deductions_recorded = EcoAccountDeduction.objects.none()
|
||||
queryset_has_deductions = EcoAccountDeduction.objects.none()
|
||||
queryset_deductions_count = -1
|
||||
queryset_deductions_recorded_count = -1
|
||||
queryset_has_deductions_count = -1
|
||||
|
||||
# Total size of deductions
|
||||
deductions_sq_m = -1
|
||||
recorded_deductions_sq_m = -1
|
||||
|
||||
def __init__(self, id: str, date_from: str, date_to: str):
|
||||
# First fetch all eco account for this office
|
||||
self.queryset = EcoAccount.objects.filter(
|
||||
responsible__conservation_office__id=id,
|
||||
deleted=None,
|
||||
created__timestamp__gte=date_from,
|
||||
created__timestamp__lte=date_to,
|
||||
)
|
||||
self.queryset_recorded = self.queryset.filter(
|
||||
recorded__isnull=False
|
||||
)
|
||||
# Fetch all related deductions
|
||||
self.queryset_deductions = EcoAccountDeduction.objects.filter(
|
||||
account__id__in=self.queryset.values_list("id")
|
||||
)
|
||||
# Fetch deductions for interventions which are already recorded
|
||||
self.queryset_deductions_recorded = self.queryset_deductions.filter(
|
||||
intervention__recorded__isnull=False
|
||||
)
|
||||
|
||||
self.queryset_count = self.queryset.count()
|
||||
self.queryset_recorded_count = self.queryset_recorded.count()
|
||||
self.queryset_deductions_count = self.queryset_deductions.count()
|
||||
self.queryset_deductions_recorded_count = self.queryset_deductions_recorded.count()
|
||||
self.queryset_has_deductions_count = self.queryset_has_deductions.count()
|
||||
|
||||
self._create_report()
|
||||
self._define_excel_map()
|
||||
|
||||
def _define_excel_map(self):
|
||||
""" Define the excel map, which holds values for each placeholder used in the template
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
self.excel_map = {
|
||||
"acc_total": self.queryset_count,
|
||||
"acc_recorded": self.queryset_recorded_count,
|
||||
"acc_deduc_recorded": self.queryset_deductions_recorded_count,
|
||||
"acc_deduc_surface_recorded": self.recorded_deductions_sq_m,
|
||||
"acc_deduc_total": self.queryset_deductions_count,
|
||||
"acc_deduc_surface_total": self.deductions_sq_m,
|
||||
}
|
||||
|
||||
def _create_report(self):
|
||||
""" Creates all report information
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
self._evaluate_deductions()
|
||||
|
||||
def _evaluate_deductions(self):
|
||||
self.deductions_sq_m = self.queryset_deductions.aggregate(
|
||||
sum=Sum("surface")
|
||||
)["sum"] or 0
|
||||
self.recorded_deductions_sq_m = self.queryset_deductions_recorded.aggregate(
|
||||
sum=Sum("surface")
|
||||
)["sum"] or 0
|
||||
|
||||
class OldDataReport:
|
||||
"""
|
||||
Evaluates 'old data' (registered (zugelassen) before 16.06.2018)
|
||||
"""
|
||||
queryset_intervention = Intervention.objects.none()
|
||||
queryset_intervention_recorded = Intervention.objects.none()
|
||||
queryset_intervention_count = -1
|
||||
queryset_intervention_recorded_count = -1
|
||||
|
||||
queryset_comps = Compensation.objects.none()
|
||||
queryset_comps_recorded = Compensation.objects.none()
|
||||
queryset_comps_count = -1
|
||||
queryset_comps_recorded_count = -1
|
||||
|
||||
queryset_acc = EcoAccount.objects.none()
|
||||
queryset_acc_recorded = EcoAccount.objects.none()
|
||||
queryset_acc_count = -1
|
||||
queryset_acc_recorded_count = -1
|
||||
|
||||
def __init__(self, id: str, date_from: str, date_to: str):
|
||||
self.queryset_intervention = Intervention.objects.filter(
|
||||
legal__registration_date__lte=LKOMPVZVO_PUBLISH_DATE,
|
||||
responsible__conservation_office__id=id,
|
||||
deleted=None,
|
||||
created__timestamp__gte=date_from,
|
||||
created__timestamp__lte=date_to,
|
||||
)
|
||||
self.queryset_intervention_recorded = self.queryset_intervention.filter(
|
||||
recorded__isnull=False
|
||||
)
|
||||
self.queryset_intervention_count = self.queryset_intervention.count()
|
||||
self.queryset_intervention_recorded_count = self.queryset_intervention_recorded.count()
|
||||
|
||||
self.queryset_comps = Compensation.objects.filter(
|
||||
intervention__in=self.queryset_intervention
|
||||
)
|
||||
self.queryset_comps_recorded = Compensation.objects.filter(
|
||||
intervention__in=self.queryset_intervention_recorded,
|
||||
)
|
||||
self.queryset_comps_count = self.queryset_comps.count()
|
||||
self.queryset_comps_recorded_count = self.queryset_comps_recorded.count()
|
||||
|
||||
self.queryset_acc = EcoAccount.objects.filter(
|
||||
legal__registration_date__lte=LKOMPVZVO_PUBLISH_DATE,
|
||||
responsible__conservation_office__id=id,
|
||||
deleted=None,
|
||||
created__timestamp__gte=date_from,
|
||||
created__timestamp__lte=date_to,
|
||||
)
|
||||
self.queryset_acc_recorded = self.queryset_acc.filter(
|
||||
recorded__isnull=False,
|
||||
)
|
||||
self.queryset_acc_count = self.queryset_acc.count()
|
||||
self.queryset_acc_recorded_count = self.queryset_acc_recorded.count()
|
||||
self._define_excel_map()
|
||||
|
||||
def _define_excel_map(self):
|
||||
""" Define the excel map, which holds values for each placeholder used in the template
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
self.excel_map = {
|
||||
"old_i_recorded": self.queryset_intervention_recorded_count,
|
||||
"old_i_total": self.queryset_intervention_count,
|
||||
"old_c_recorded": self.queryset_comps_recorded_count,
|
||||
"old_c_total": self.queryset_comps_count,
|
||||
"old_ea_recorded": self.queryset_acc_recorded_count,
|
||||
"old_ea_total": self.queryset_acc_count,
|
||||
}
|
||||
|
||||
def __init__(self, office_id: str, date_from: str, date_to: str):
|
||||
self.office_id = office_id
|
||||
self.date_from = date_from
|
||||
self.date_to = date_to
|
||||
|
||||
self.intervention_report = self.InterventionReport(self.office_id, date_from, date_to)
|
||||
self.compensation_report = self.CompensationReport(self.office_id, date_from, date_to)
|
||||
self.eco_account_report = self.EcoAccountReport(self.office_id, date_from, date_to)
|
||||
self.old_data_report = self.OldDataReport(self.office_id, date_from, date_to)
|
||||
|
||||
# Build excel map
|
||||
self.excel_map = {
|
||||
"date_from": date_from.strftime(DEFAULT_DATE_FORMAT),
|
||||
"date_to": date_to.strftime(DEFAULT_DATE_FORMAT),
|
||||
}
|
||||
self.excel_map.update(self.intervention_report.excel_map)
|
||||
self.excel_map.update(self.compensation_report.excel_map)
|
||||
self.excel_map.update(self.eco_account_report.excel_map)
|
||||
self.excel_map.update(self.old_data_report.excel_map)
|
98
analysis/views.py
Normal file
98
analysis/views.py
Normal file
@ -0,0 +1,98 @@
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.utils import timezone
|
||||
|
||||
from analysis.forms import TimespanReportForm
|
||||
from analysis.utils.excel.excel import TempExcelFile
|
||||
from analysis.utils.report import TimespanReport
|
||||
from codelist.models import KonovaCode
|
||||
from konova.contexts import BaseContext
|
||||
from konova.decorators import conservation_office_group_required
|
||||
from konova.utils.message_templates import FORM_INVALID, PARAMS_INVALID
|
||||
|
||||
|
||||
@login_required
|
||||
@conservation_office_group_required
|
||||
def index_reports_view(request: HttpRequest):
|
||||
"""
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
template = "analysis/reports/index.html"
|
||||
form = TimespanReportForm(request.POST or None)
|
||||
if request.method == "POST":
|
||||
if form.is_valid():
|
||||
redirect_url = form.save()
|
||||
return redirect(redirect_url)
|
||||
else:
|
||||
messages.error(
|
||||
request,
|
||||
FORM_INVALID,
|
||||
extra_tags="danger",
|
||||
)
|
||||
context = {
|
||||
"form": form
|
||||
}
|
||||
context = BaseContext(request, context).context
|
||||
return render(request, template, context)
|
||||
|
||||
|
||||
@login_required
|
||||
@conservation_office_group_required
|
||||
def detail_report_view(request: HttpRequest, id: str):
|
||||
""" Renders the detailed report for a conservation office
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
id (str): The conservation_office KonovaCode id
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
# Try to resolve the requested office id
|
||||
cons_office = get_object_or_404(
|
||||
KonovaCode,
|
||||
id=id
|
||||
)
|
||||
# Try to resolve the date parameters into Date objects -> redirect if this fails
|
||||
try:
|
||||
df = request.GET.get("df", None)
|
||||
dt = request.GET.get("dt", None)
|
||||
date_from = timezone.make_aware(timezone.datetime.fromisoformat(df))
|
||||
date_to = timezone.make_aware(timezone.datetime.fromisoformat(dt))
|
||||
except ValueError:
|
||||
messages.error(
|
||||
request,
|
||||
PARAMS_INVALID,
|
||||
extra_tags="danger",
|
||||
)
|
||||
return redirect("analysis:reports")
|
||||
|
||||
# Check whether the html default rendering is requested or an alternative
|
||||
format_param = request.GET.get("format", "html")
|
||||
report = TimespanReport(id, date_from, date_to)
|
||||
|
||||
if format_param == "html":
|
||||
template = "analysis/reports/detail.html"
|
||||
context = {
|
||||
"office": cons_office,
|
||||
"report": report,
|
||||
}
|
||||
context = BaseContext(request, context).context
|
||||
return render(request, template, context)
|
||||
elif format_param == "excel":
|
||||
file = TempExcelFile(report.excel_template_path, report.excel_map)
|
||||
response = HttpResponse(
|
||||
content=file.stream,
|
||||
content_type="application/ms-excel",
|
||||
)
|
||||
response['Content-Disposition'] = f'attachment; filename={cons_office.long_name}_{df}_{dt}.xlsx'
|
||||
return response
|
||||
else:
|
||||
raise NotImplementedError
|
@ -16,7 +16,7 @@ from codelist.models import KonovaCode
|
||||
from codelist.settings import CODELIST_COMPENSATION_FUNDING_ID, CODELIST_CONSERVATION_OFFICE_ID
|
||||
from compensation.models import Compensation, EcoAccount
|
||||
from intervention.inputs import GenerateInput
|
||||
from intervention.models import Intervention, ResponsibilityData
|
||||
from intervention.models import Intervention, ResponsibilityData, LegalData
|
||||
from konova.forms import BaseForm, SimpleGeomForm
|
||||
from user.models import UserActionLogEntry, UserAction
|
||||
|
||||
@ -284,10 +284,40 @@ class NewEcoAccountForm(AbstractCompensationForm, CompensationResponsibleFormMix
|
||||
Inherits from basic AbstractCompensationForm and further form fields from CompensationResponsibleFormMixin
|
||||
|
||||
"""
|
||||
surface = forms.DecimalField(
|
||||
min_value=0.00,
|
||||
decimal_places=2,
|
||||
label=_("Available Surface"),
|
||||
label_suffix="",
|
||||
required=False,
|
||||
help_text=_("The amount that can be used for deductions"),
|
||||
widget=forms.NumberInput(
|
||||
attrs={
|
||||
"class": "form-control",
|
||||
"placeholder": "0,00"
|
||||
}
|
||||
)
|
||||
)
|
||||
registration_date = forms.DateField(
|
||||
label=_("Agreement date"),
|
||||
label_suffix="",
|
||||
help_text=_("When did the parties agree on this?"),
|
||||
required=False,
|
||||
widget=forms.DateInput(
|
||||
attrs={
|
||||
"type": "date",
|
||||
"class": "form-control",
|
||||
},
|
||||
format="%d.%m.%Y"
|
||||
)
|
||||
)
|
||||
|
||||
field_order = [
|
||||
"identifier",
|
||||
"title",
|
||||
"conservation_office",
|
||||
"registration_date",
|
||||
"surface",
|
||||
"conservation_file_number",
|
||||
"handler",
|
||||
"fundings",
|
||||
@ -313,7 +343,9 @@ class NewEcoAccountForm(AbstractCompensationForm, CompensationResponsibleFormMix
|
||||
identifier = self.cleaned_data.get("identifier", None)
|
||||
title = self.cleaned_data.get("title", None)
|
||||
fundings = self.cleaned_data.get("fundings", None)
|
||||
registration_date = self.cleaned_data.get("registration_date", None)
|
||||
handler = self.cleaned_data.get("handler", None)
|
||||
surface = self.cleaned_data.get("surface", None)
|
||||
conservation_office = self.cleaned_data.get("conservation_office", None)
|
||||
conservation_file_number = self.cleaned_data.get("conservation_file_number", None)
|
||||
comment = self.cleaned_data.get("comment", None)
|
||||
@ -332,15 +364,20 @@ class NewEcoAccountForm(AbstractCompensationForm, CompensationResponsibleFormMix
|
||||
conservation_office=conservation_office,
|
||||
)
|
||||
|
||||
legal = LegalData.objects.create(
|
||||
registration_date=registration_date
|
||||
)
|
||||
|
||||
# Finally create main object
|
||||
acc = EcoAccount.objects.create(
|
||||
identifier=identifier,
|
||||
title=title,
|
||||
responsible=responsible,
|
||||
deductable_surface=0.00,
|
||||
deductable_surface=surface,
|
||||
created=action,
|
||||
geometry=geometry,
|
||||
comment=comment,
|
||||
legal=legal
|
||||
)
|
||||
acc.fundings.set(fundings)
|
||||
acc.users.add(user)
|
||||
@ -354,30 +391,6 @@ class EditEcoAccountForm(NewEcoAccountForm):
|
||||
""" Form for editing eco accounts
|
||||
|
||||
"""
|
||||
surface = forms.DecimalField(
|
||||
min_value=0.00,
|
||||
decimal_places=2,
|
||||
label=_("Available Surface"),
|
||||
label_suffix="",
|
||||
required=False,
|
||||
help_text=_("The amount that can be used for deductions"),
|
||||
widget=forms.NumberInput(
|
||||
attrs={
|
||||
"class": "form-control",
|
||||
"placeholder": "0,00"
|
||||
}
|
||||
)
|
||||
)
|
||||
field_order = [
|
||||
"identifier",
|
||||
"title",
|
||||
"conservation_office",
|
||||
"surface",
|
||||
"conservation_file_number",
|
||||
"handler",
|
||||
"fundings",
|
||||
"comment",
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
@ -387,11 +400,15 @@ class EditEcoAccountForm(NewEcoAccountForm):
|
||||
self.cancel_redirect = reverse("compensation:acc-detail", args=(self.instance.id,))
|
||||
|
||||
# Initialize form data
|
||||
reg_date = self.instance.legal.registration_date
|
||||
if reg_date is not None:
|
||||
reg_date = reg_date.isoformat()
|
||||
form_data = {
|
||||
"identifier": self.instance.identifier,
|
||||
"title": self.instance.title,
|
||||
"surface": self.instance.deductable_surface,
|
||||
"handler": self.instance.responsible.handler,
|
||||
"registration_date": reg_date,
|
||||
"conservation_office": self.instance.responsible.conservation_office,
|
||||
"conservation_file_number": self.instance.responsible.conservation_file_number,
|
||||
"fundings": self.instance.fundings.all(),
|
||||
@ -409,6 +426,7 @@ class EditEcoAccountForm(NewEcoAccountForm):
|
||||
identifier = self.cleaned_data.get("identifier", None)
|
||||
title = self.cleaned_data.get("title", None)
|
||||
fundings = self.cleaned_data.get("fundings", None)
|
||||
registration_date = self.cleaned_data.get("registration_date", None)
|
||||
handler = self.cleaned_data.get("handler", None)
|
||||
surface = self.cleaned_data.get("surface", None)
|
||||
conservation_office = self.cleaned_data.get("conservation_office", None)
|
||||
@ -429,6 +447,10 @@ class EditEcoAccountForm(NewEcoAccountForm):
|
||||
self.instance.responsible.conservation_file_number = conservation_file_number
|
||||
self.instance.responsible.save()
|
||||
|
||||
# Update legal data
|
||||
self.instance.legal.registration_date = registration_date
|
||||
self.instance.legal.save()
|
||||
|
||||
# Update main oject data
|
||||
self.instance.identifier = identifier
|
||||
self.instance.title = title
|
||||
|
@ -19,7 +19,7 @@ from codelist.settings import CODELIST_COMPENSATION_ACTION_ID, CODELIST_BIOTOPES
|
||||
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, LegalData
|
||||
from konova.models import BaseObject, BaseResource, Geometry, UuidModel, AbstractDocument, \
|
||||
generate_document_file_upload_path
|
||||
from konova.settings import DEFAULT_SRID_RLP, LANIS_LINK_TEMPLATE
|
||||
@ -309,6 +309,14 @@ class EcoAccount(AbstractCompensation):
|
||||
default=0,
|
||||
)
|
||||
|
||||
legal = models.OneToOneField(
|
||||
LegalData,
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Holds data on legal dates or law"
|
||||
)
|
||||
|
||||
objects = EcoAccountManager()
|
||||
|
||||
def __str__(self):
|
||||
|
@ -61,6 +61,10 @@
|
||||
<th scope="row">{% trans 'Conservation office file number' %}</th>
|
||||
<td class="align-middle">{{obj.responsible.conservation_file_number|default_if_none:""}}</td>
|
||||
</tr>
|
||||
<tr {% if not obj.legal.registration_date %}class="alert alert-danger" title="{% trans 'Missing' %}" {% endif %}>
|
||||
<th scope="row">{% trans 'Agreement date' %}</th>
|
||||
<td class="align-middle">{{obj.legal.registration_date|default_if_none:""}}</td>
|
||||
</tr>
|
||||
<tr {% if not obj.responsible.handler %}class="alert alert-danger" title="{% trans 'Missing' %}" {% endif %}>
|
||||
<th scope="row">{% trans 'Action handler' %}</th>
|
||||
<td class="align-middle">{{obj.responsible.handler|default_if_none:""}}</td>
|
||||
|
@ -77,7 +77,7 @@ def new_view(request: HttpRequest, intervention_id: str = None):
|
||||
messages.success(request, _("Compensation {} added").format(comp.identifier))
|
||||
return redirect("compensation:detail", id=comp.id)
|
||||
else:
|
||||
messages.error(request, FORM_INVALID)
|
||||
messages.error(request, FORM_INVALID, extra_tags="danger",)
|
||||
else:
|
||||
# For clarification: nothing in this case
|
||||
pass
|
||||
@ -132,7 +132,7 @@ def edit_view(request: HttpRequest, id: str):
|
||||
messages.success(request, _("Compensation {} edited").format(comp.identifier))
|
||||
return redirect("compensation:detail", id=comp.id)
|
||||
else:
|
||||
messages.error(request, FORM_INVALID)
|
||||
messages.error(request, FORM_INVALID, extra_tags="danger",)
|
||||
else:
|
||||
# For clarification: nothing in this case
|
||||
pass
|
||||
|
@ -86,7 +86,7 @@ def new_view(request: HttpRequest):
|
||||
messages.success(request, _("Eco-Account {} added").format(acc.identifier))
|
||||
return redirect("compensation:acc-detail", id=acc.id)
|
||||
else:
|
||||
messages.error(request, FORM_INVALID)
|
||||
messages.error(request, FORM_INVALID, extra_tags="danger",)
|
||||
else:
|
||||
# For clarification: nothing in this case
|
||||
pass
|
||||
@ -141,7 +141,7 @@ def edit_view(request: HttpRequest, id: str):
|
||||
messages.success(request, _("Eco-Account {} edited").format(acc.identifier))
|
||||
return redirect("compensation:acc-detail", id=acc.id)
|
||||
else:
|
||||
messages.error(request, FORM_INVALID)
|
||||
messages.error(request, FORM_INVALID, extra_tags="danger",)
|
||||
else:
|
||||
# For clarification: nothing in this case
|
||||
pass
|
||||
|
@ -78,7 +78,7 @@ def new_view(request: HttpRequest):
|
||||
messages.success(request, _("EMA {} added").format(ema.identifier))
|
||||
return redirect("ema:detail", id=ema.id)
|
||||
else:
|
||||
messages.error(request, FORM_INVALID)
|
||||
messages.error(request, FORM_INVALID, extra_tags="danger",)
|
||||
else:
|
||||
# For clarification: nothing in this case
|
||||
pass
|
||||
@ -202,7 +202,7 @@ def edit_view(request: HttpRequest, id: str):
|
||||
messages.success(request, _("EMA {} edited").format(ema.identifier))
|
||||
return redirect("ema:detail", id=ema.id)
|
||||
else:
|
||||
messages.error(request, FORM_INVALID)
|
||||
messages.error(request, FORM_INVALID, extra_tags="danger",)
|
||||
else:
|
||||
# For clarification: nothing in this case
|
||||
pass
|
||||
|
@ -23,26 +23,3 @@ class InterventionManager(models.Manager):
|
||||
).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",
|
||||
)
|
||||
|
@ -15,7 +15,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
from codelist.models import KonovaCode
|
||||
from codelist.settings import CODELIST_REGISTRATION_OFFICE_ID, CODELIST_CONSERVATION_OFFICE_ID, CODELIST_LAW_ID, \
|
||||
CODELIST_PROCESS_TYPE_ID
|
||||
from intervention.managers import InterventionManager, LegalDataManager, ResponsibilityDataManager
|
||||
from intervention.managers import InterventionManager
|
||||
from konova.models import BaseObject, Geometry, UuidModel, BaseResource, AbstractDocument, \
|
||||
generate_document_file_upload_path
|
||||
from konova.settings import DEFAULT_SRID_RLP, LANIS_LINK_TEMPLATE, LANIS_ZOOM_LUT
|
||||
@ -56,7 +56,6 @@ class ResponsibilityData(UuidModel):
|
||||
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'")
|
||||
|
||||
objects = ResponsibilityDataManager()
|
||||
|
||||
def __str__(self):
|
||||
return "ZB: {} | ETS: {} | Handler: {}".format(
|
||||
@ -172,8 +171,6 @@ class LegalData(UuidModel):
|
||||
|
||||
revocation = models.OneToOneField(Revocation, null=True, blank=True, help_text="Refers to 'Widerspruch am'", on_delete=models.SET_NULL)
|
||||
|
||||
objects = LegalDataManager()
|
||||
|
||||
|
||||
class Intervention(BaseObject):
|
||||
"""
|
||||
|
@ -79,7 +79,7 @@ def new_view(request: HttpRequest):
|
||||
messages.success(request, _("Intervention {} added").format(intervention.identifier))
|
||||
return redirect("intervention:detail", id=intervention.id)
|
||||
else:
|
||||
messages.error(request, FORM_INVALID)
|
||||
messages.error(request, FORM_INVALID, extra_tags="danger",)
|
||||
else:
|
||||
# For clarification: nothing in this case
|
||||
pass
|
||||
@ -264,7 +264,7 @@ def edit_view(request: HttpRequest, id: str):
|
||||
messages.success(request, _("Intervention {} edited").format(intervention.identifier))
|
||||
return redirect("intervention:detail", id=intervention.id)
|
||||
else:
|
||||
messages.error(request, FORM_INVALID)
|
||||
messages.error(request, FORM_INVALID, extra_tags="danger",)
|
||||
else:
|
||||
# For clarification: nothing in this case
|
||||
pass
|
||||
|
@ -37,12 +37,14 @@ class BaseForm(forms.Form):
|
||||
"""
|
||||
template = None
|
||||
action_url = None
|
||||
action_btn_label = _("Save")
|
||||
form_title = None
|
||||
cancel_redirect = None
|
||||
form_caption = None
|
||||
instance = None # The data holding model object
|
||||
form_attrs = {} # Holds additional attributes, that can be used in the template
|
||||
has_required_fields = False # Automatically set. Triggers hint rendering in templates
|
||||
show_cancel_btn = True
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.instance = kwargs.pop("instance", None)
|
||||
@ -189,6 +191,7 @@ class BaseModalForm(BaseForm, BSModalForm):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.action_btn_label = _("Continue")
|
||||
|
||||
def process_request(self, request: HttpRequest, msg_success: str = _("Object removed"), msg_error: str = FORM_INVALID, redirect_url: str = None):
|
||||
""" Generic processing of request
|
||||
|
@ -69,6 +69,7 @@ INSTALLED_APPS = [
|
||||
'user',
|
||||
'ema',
|
||||
'codelist',
|
||||
'analysis',
|
||||
]
|
||||
if DEBUG:
|
||||
INSTALLED_APPS += [
|
||||
|
@ -34,3 +34,19 @@ def bootstrap_cls(value):
|
||||
|
||||
"""
|
||||
return SVI_BOOTSTRAP_CLS_MAP.get(value, "")
|
||||
|
||||
|
||||
@register.filter("default_if_zero")
|
||||
def default_if_zero(val1, val2):
|
||||
""" Returns val2 if val1 is 0
|
||||
|
||||
Similar to default_if_none
|
||||
|
||||
Args:
|
||||
val1 (int): The numerical value
|
||||
val2 (str): The alternative
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return val1 if val1 > 0 else val2
|
||||
|
@ -36,7 +36,8 @@ urlpatterns = [
|
||||
path('ema/', include("ema.urls")),
|
||||
path('user/', include("user.urls")),
|
||||
path('news/', include("news.urls")),
|
||||
path('news/', include("codelist.urls")),
|
||||
path('cl/', include("codelist.urls")),
|
||||
path('analysis/', include("analysis.urls")),
|
||||
|
||||
# Generic deadline routes
|
||||
path('deadline/<id>/remove', remove_deadline_view, name="deadline-remove"),
|
||||
|
@ -9,6 +9,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
FORM_INVALID = _("There was an error on this form.")
|
||||
PARAMS_INVALID = _("Invalid parameters")
|
||||
INTERVENTION_INVALID = _("There are errors in this intervention.")
|
||||
IDENTIFIER_REPLACED = _("The identifier '{}' had to be changed to '{}' since another entry has been added in the meanwhile, which uses this identifier")
|
||||
DATA_UNSHARED = _("This data is not shared with you")
|
||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -11,11 +11,14 @@ django-filter==2.4.0
|
||||
django-fontawesome-5==1.0.18
|
||||
django-simple-sso==1.1.0
|
||||
django-tables2==2.3.4
|
||||
et-xmlfile==1.1.0
|
||||
idna==2.10
|
||||
importlib-metadata==2.1.1
|
||||
itsdangerous
|
||||
psycopg2-binary
|
||||
itsdangerous==0.24
|
||||
openpyxl==3.0.9
|
||||
psycopg2-binary==2.9.1
|
||||
pytz==2020.4
|
||||
qrcode==7.3.1
|
||||
requests==2.25.0
|
||||
six==1.15.0
|
||||
soupsieve==2.2.1
|
||||
|
@ -19,12 +19,14 @@
|
||||
{% include 'form/table/generic_table_form_body.html' %}
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
{% if form.show_cancel_btn %}
|
||||
<a href="{{ form.cancel_redirect }}">
|
||||
<button class="btn btn-default" type="button" title="{% trans 'Cancel' %}">{% trans 'Cancel' %}</button>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-6 d-flex justify-content-end">
|
||||
<button class="btn btn-default" type="submit" title="{% trans 'Save' %}">{% trans 'Save' %}</button>
|
||||
<button class="btn btn-default" type="submit" title="{{form.action_btn_label}}">{{form.action_btn_label}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -22,7 +22,7 @@
|
||||
</div>
|
||||
{% if form.render_submit %}
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-default">{% trans 'Continue' %}</button>
|
||||
<button type="submit" class="btn btn-default" title="{{form.action_btn_label}}">{{form.action_btn_label}}</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</form>
|
@ -43,7 +43,7 @@
|
||||
<a class="dropdown-item" href="{% url 'ema:index' %}" title="{% trans 'Payment funded compensations' %}">{% fa5_icon 'euro-sign' %} {% trans 'EMA' %}</a>
|
||||
<a class="dropdown-item" href="{% url 'home' %}">{% fa5_icon 'file-import' %} {% trans 'Import...' %}</a>
|
||||
<a class="dropdown-item" href="{% url 'home' %}">{% fa5_icon 'file-export' %} {% trans 'Export...' %}</a>
|
||||
<a class="dropdown-item" href="{% url 'home' %}">{% fa5_icon 'file-alt' %} {% trans 'Reports' %}</a>
|
||||
<a class="dropdown-item" href="{% url 'analysis:reports' %}">{% fa5_icon 'file-alt' %} {% trans 'Reports' %}</a>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user