Merge branch 'master' into 132_Old_entries

# Conflicts:
#	konova/management/commands/update_all_parcels.py
#	konova/urls.py
#	user/admin.py
This commit is contained in:
2022-06-01 13:12:35 +02:00
160 changed files with 62451 additions and 794 deletions

View File

@@ -12,14 +12,15 @@ from django.db import transaction
from django.urls import reverse, reverse_lazy
from django.utils.translation import gettext_lazy as _
from compensation.forms.forms import AbstractCompensationForm, CompensationResponsibleFormMixin
from compensation.forms.forms import AbstractCompensationForm, CompensationResponsibleFormMixin, \
PikCompensationFormMixin
from ema.models import Ema, EmaDocument
from intervention.models import Responsibility, Handler
from konova.forms import SimpleGeomForm, NewDocumentModalForm
from user.models import UserActionLogEntry
class NewEmaForm(AbstractCompensationForm, CompensationResponsibleFormMixin):
class NewEmaForm(AbstractCompensationForm, CompensationResponsibleFormMixin, PikCompensationFormMixin):
""" Form for creating new EMA objects.
Inherits basic form fields from AbstractCompensationForm and additional from CompensationResponsibleFormMixin.
@@ -31,6 +32,7 @@ class NewEmaForm(AbstractCompensationForm, CompensationResponsibleFormMixin):
"title",
"conservation_office",
"conservation_file_number",
"is_pik",
"handler_type",
"handler_detail",
"comment",
@@ -58,6 +60,7 @@ class NewEmaForm(AbstractCompensationForm, CompensationResponsibleFormMixin):
handler_detail = self.cleaned_data.get("handler_detail", None)
conservation_office = self.cleaned_data.get("conservation_office", None)
conservation_file_number = self.cleaned_data.get("conservation_file_number", None)
is_pik = self.cleaned_data.get("is_pik", None)
comment = self.cleaned_data.get("comment", None)
# Create log entry
@@ -83,6 +86,7 @@ class NewEmaForm(AbstractCompensationForm, CompensationResponsibleFormMixin):
created=action,
geometry=geometry,
comment=comment,
is_pik=is_pik,
)
# Add the creating user to the list of shared users
@@ -116,6 +120,7 @@ class EditEmaForm(NewEmaForm):
"conservation_office": self.instance.responsible.conservation_office,
"conservation_file_number": self.instance.responsible.conservation_file_number,
"comment": self.instance.comment,
"is_pik": self.instance.is_pik,
}
disabled_fields = []
self.load_initial_data(
@@ -133,6 +138,7 @@ class EditEmaForm(NewEmaForm):
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)
is_pik = self.cleaned_data.get("is_pik", None)
# Create log entry
action = UserActionLogEntry.get_edited_action(user)
@@ -152,6 +158,7 @@ class EditEmaForm(NewEmaForm):
self.instance.title = title
self.instance.geometry = geometry
self.instance.comment = comment
self.instance.is_pik = is_pik
self.instance.modified = action
self.instance.save()

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.1.3 on 2022-05-31 10:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('ema', '0003_ema_teams'),
]
operations = [
migrations.AddField(
model_name='ema',
name='is_pik',
field=models.BooleanField(blank=True, default=False, help_text="Flag if compensation is a 'Produktonsintegrierte Kompensation'", null=True),
),
]

View File

@@ -13,15 +13,14 @@ from django.db.models import QuerySet
from django.http import HttpRequest
from django.urls import reverse
from compensation.models import AbstractCompensation
from compensation.models import AbstractCompensation, PikMixin
from ema.managers import EmaManager
from ema.utils.quality import EmaQualityChecker
from konova.models import AbstractDocument, generate_document_file_upload_path, RecordableObjectMixin, ShareableObjectMixin
from konova.settings import DEFAULT_SRID_RLP, LANIS_LINK_TEMPLATE
from konova.utils.message_templates import DATA_UNSHARED_EXPLANATION, DOCUMENT_REMOVED_TEMPLATE
class Ema(AbstractCompensation, ShareableObjectMixin, RecordableObjectMixin):
class Ema(AbstractCompensation, ShareableObjectMixin, RecordableObjectMixin, PikMixin):
"""
EMA = Ersatzzahlungsmaßnahme
(compensation actions from payments)

View File

@@ -104,7 +104,7 @@ class EmaTable(BaseTable, TableRenderMixin):
"""
parcels = value.get_underlying_parcels().values_list(
"gmrkng",
"parcel_group__name",
flat=True
).distinct()
html = render_to_string(
@@ -115,7 +115,6 @@ class EmaTable(BaseTable, TableRenderMixin):
)
return html
def render_r(self, value, record: Ema):
""" Renders the registered column for a EMA
@@ -130,9 +129,7 @@ class EmaTable(BaseTable, TableRenderMixin):
recorded = value is not None
tooltip = _("Not recorded yet")
if recorded:
value = value.timestamp
value = localtime(value)
on = value.strftime(DEFAULT_DATE_TIME_FORMAT)
on = value.get_timestamp_str_formatted()
tooltip = _("Recorded on {} by {}").format(on, record.recorded.user)
html += self.render_bookmark(
tooltip=tooltip,
@@ -151,9 +148,7 @@ class EmaTable(BaseTable, TableRenderMixin):
"""
html = ""
has_access = value.filter(
id=self.user.id
).exists()
has_access = record.is_shared_with(self.user)
html += self.render_icn(
tooltip=_("Full access granted") if has_access else _("Access not granted"),

View File

@@ -56,29 +56,38 @@
<th scope="row">{% trans 'Action handler' %}</th>
<td class="align-middle">{{obj.responsible.handler|default_if_none:""}}</td>
</tr>
<tr>
<th scope="row">{% trans 'Is PIK' %}</th>
<td class="align-middle">
{% if obj.is_pik %}
{% trans 'Yes' %}
{% else %}
{% trans 'No' %}
{% endif %}
</td>
</tr>
<tr>
<th scope="row">{% trans 'Last modified' %}</th>
<td class="align-middle">
{% if obj.modified %}
{{obj.modified.timestamp|default_if_none:""|naturalday}}
{{obj.modified.timestamp|default_if_none:""}}
<br>
{{obj.modified.user.username}}
{% else %}
{{obj.created.timestamp|default_if_none:""|naturalday}}
{{obj.created.timestamp|default_if_none:""}}
<br>
{{obj.created.user.username}}
{% endif %}
</td>
</tr>
<tr>
<th scope="row">{% trans 'Shared with' %}</th>
<td class="align-middle">
{% for team in obj.teams.all %}
{% for team in obj.shared_teams %}
{% include 'user/includes/team_data_modal_button.html' %}
{% endfor %}
<hr>
{% for user in obj.users.all %}
{% for user in obj.user.all %}
{% include 'user/includes/contact_modal_button.html' %}
{% endfor %}
</td>
@@ -88,10 +97,12 @@
</div>
<div class="col-sm-12 col-md-12 col-lg-12 col-xl-6">
<div class="row">
{% include 'map/geom_form.html' %}
<div class="col-sm-12">
{% include 'map/geom_form.html' %}
</div>
</div>
<div class="row">
{% include 'konova/includes/parcels.html' %}
{% include 'konova/includes/parcels/parcels.html' %}
</div>
<div class="row">
{% include 'konova/includes/comment_card.html' %}

View File

@@ -20,6 +20,16 @@
<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 'Is PIK' %}</th>
<td class="align-middle">
{% if obj.is_pik %}
{% trans 'Yes' %}
{% else %}
{% trans 'No' %}
{% endif %}
</td>
</tr>
<tr>
<th scope="row">{% trans 'Last modified' %}</th>
<td class="align-middle">
@@ -35,20 +45,15 @@
</div>
<div class="col-sm-12 col-md-12 col-lg-12 col-xl-6">
<div class="row">
{% include 'map/geom_form.html' %}
<div class="col-sm-12">
{% include 'map/geom_form.html' %}
</div>
</div>
<div class="row">
{% include 'konova/includes/parcels.html' %}
{% include 'konova/includes/parcels/parcels.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>
{% include 'konova/includes/report/qrcodes.html' %}
</div>
</div>

View File

@@ -41,11 +41,12 @@ class EmaWorkflowTestCase(BaseWorkflowTestCase):
test_id = self.create_dummy_string()
test_title = self.create_dummy_string()
test_geom = self.create_dummy_geometry()
geom_json = self.create_geojson(test_geom)
test_conservation_office = self.get_conservation_office_code()
post_data = {
"identifier": test_id,
"title": test_title,
"geom": test_geom.geojson,
"geom": geom_json,
"conservation_office": test_conservation_office.id
}
self.client_user.post(new_url, post_data)
@@ -117,6 +118,32 @@ class EmaWorkflowTestCase(BaseWorkflowTestCase):
self.assertEqual(pre_edit_log_count + 1, self.ema.log.count())
self.assertEqual(self.ema.log.first().action, UserAction.EDITED)
def test_non_editable_after_recording(self):
""" Tests that the EMA can not be edited after being recorded
User must be redirected to another page
Returns:
"""
self.superuser.groups.add(self.groups.get(name=ETS_GROUP))
self.assertIsNotNone(self.ema)
self.ema.share_with_user(self.superuser)
self.assertFalse(self.ema.is_recorded)
edit_url = reverse("ema:edit", args=(self.ema.id,))
response = self.client_user.get(edit_url)
has_redirect = response.status_code == 302
self.assertFalse(has_redirect)
self.ema.set_recorded(self.superuser)
self.assertTrue(self.ema.is_recorded)
edit_url = reverse("ema:edit", args=(self.ema.id,))
response = self.client_user.get(edit_url)
has_redirect = response.status_code == 302
self.assertTrue(has_redirect)
self.ema.set_unrecorded(self.superuser)
def test_recordability(self):
"""
This tests if the recordability of the Ema is triggered by the quality of it's data (e.g. not all fields filled)

View File

@@ -26,7 +26,7 @@ from konova.utils.generators import generate_qr_code
from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID, DATA_UNSHARED, DATA_UNSHARED_EXPLANATION, \
DOCUMENT_ADDED, COMPENSATION_STATE_REMOVED, COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, \
COMPENSATION_ACTION_ADDED, DEADLINE_ADDED, DEADLINE_REMOVED, DOCUMENT_EDITED, COMPENSATION_STATE_EDITED, \
COMPENSATION_ACTION_EDITED, DEADLINE_EDITED
COMPENSATION_ACTION_EDITED, DEADLINE_EDITED, RECORDED_BLOCKS_EDIT
from konova.utils.user_checks import in_group
@@ -213,6 +213,13 @@ def edit_view(request: HttpRequest, id: str):
template = "compensation/form/view.html"
# Get object from db
ema = get_object_or_404(Ema, id=id)
if ema.is_recorded:
messages.info(
request,
RECORDED_BLOCKS_EDIT
)
return redirect("ema:detail", id=id)
# Create forms, initialize with values from db/from POST request
data_form = EditEmaForm(request.POST or None, instance=ema)
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=ema)
@@ -563,14 +570,12 @@ def report_view(request:HttpRequest, id: str):
instance=ema,
)
parcels = ema.get_underlying_parcels()
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
)
qrcode_url = request.build_absolute_uri(reverse("ema:report", args=(id,)))
qrcode_img = generate_qr_code(qrcode_url, 10)
qrcode_lanis_url = ema.get_LANIS_link()
qrcode_img_lanis = generate_qr_code(qrcode_lanis_url, 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")
@@ -578,8 +583,14 @@ def report_view(request:HttpRequest, id: str):
context = {
"obj": ema,
"qrcode": qrcode_img,
"qrcode_lanis": qrcode_img_lanis,
"qrcode": {
"img": qrcode_img,
"url": qrcode_url
},
"qrcode_lanis": {
"img": qrcode_img_lanis,
"url": qrcode_lanis_url
},
"has_access": False, # disables action buttons during rendering
"before_states": before_states,
"after_states": after_states,