* adds NewEmaForm and EditEmaForm
* refactors ResponsibilityData related form fields into reusable mixin CompensationResponsibleFormMixin
   * used in NewEcoAccountForm and NewEmaForm for easier maintaining and reducing amount of code
* refactors templates /xy/new/view.html into /xy/form/view.html since the same template file is used for new and edit forms
This commit is contained in:
mipel 2021-10-06 16:00:17 +02:00
parent 1971cf5942
commit 3842bcf0b1
11 changed files with 319 additions and 58 deletions

View File

@ -85,10 +85,61 @@ class AbstractCompensationForm(BaseForm):
abstract = True
class CompensationResponsibleFormMixin(forms.Form):
""" Encapsulates form fields used in different compensation related models like EcoAccount or EMA
"""
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")
}
),
)
conservation_file_number = forms.CharField(
label=_("Conservation office file number"),
label_suffix="",
max_length=255,
required=False,
widget=forms.TextInput(
attrs={
"placeholder": _("ETS-123/ABC.456"),
"class": "form-control",
}
)
)
handler = forms.CharField(
label=_("Eco-account handler"),
label_suffix="",
max_length=255,
required=False,
help_text=_("Who handles the eco-account"),
widget=forms.TextInput(
attrs={
"placeholder": _("Company Mustermann"),
"class": "form-control",
}
)
)
class NewCompensationForm(AbstractCompensationForm):
""" Form for creating new compensations.
Can be initialized with an intervention id for preselecting the related intervention.
form = NewCompensationForm(request.POST or None, intervention_id=intervention_id)
...
The intervention id will not be resolved into the intervention ORM object but instead will be used to initialize
the related form field.
"""
intervention = forms.ModelChoiceField(
@ -172,6 +223,9 @@ class NewCompensationForm(AbstractCompensationForm):
class EditCompensationForm(NewCompensationForm):
""" Form for editing compensations
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.form_title = _("Edit compensation")
@ -224,48 +278,12 @@ class EditCompensationForm(NewCompensationForm):
return self.instance
class NewEcoAccountForm(AbstractCompensationForm):
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")
}
),
)
conservation_file_number = forms.CharField(
label=_("Conservation office file number"),
label_suffix="",
max_length=255,
required=False,
widget=forms.TextInput(
attrs={
"placeholder": _("ETS-123/ABC.456"),
"class": "form-control",
}
)
)
handler = forms.CharField(
label=_("Eco-account handler"),
label_suffix="",
max_length=255,
required=False,
help_text=_("Who handles the eco-account"),
widget=forms.TextInput(
attrs={
"placeholder": _("Company Mustermann"),
"class": "form-control",
}
)
)
class NewEcoAccountForm(AbstractCompensationForm, CompensationResponsibleFormMixin):
""" Form for creating eco accounts
Inherits from basic AbstractCompensationForm and further form fields from CompensationResponsibleFormMixin
"""
field_order = [
"identifier",
"title",
@ -333,6 +351,9 @@ class NewEcoAccountForm(AbstractCompensationForm):
class EditEcoAccountForm(NewEcoAccountForm):
""" Form for editing eco accounts
"""
surface = forms.DecimalField(
min_value=0.00,
decimal_places=2,

View File

@ -58,7 +58,7 @@ def new_view(request: HttpRequest, intervention_id: str = None):
Returns:
"""
template = "compensation/new/view.html"
template = "compensation/form/view.html"
data_form = NewCompensationForm(request.POST or None, intervention_id=intervention_id)
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
if request.method == "POST":
@ -83,7 +83,6 @@ def new_view(request: HttpRequest, intervention_id: str = None):
context = {
"form": data_form,
"geom_form": geom_form,
"url": reverse("compensation:new-id")
}
context = BaseContext(request, context).context
return render(request, template, context)
@ -119,7 +118,7 @@ def edit_view(request: HttpRequest, id: str):
Returns:
"""
template = "compensation/new/view.html"
template = "compensation/form/view.html"
# Get object from db
comp = get_object_or_404(Compensation, id=id)
# Create forms, initialize with values from db/from POST request

View File

@ -68,7 +68,7 @@ def new_view(request: HttpRequest):
Returns:
"""
template = "compensation/new/view.html"
template = "compensation/form/view.html"
data_form = NewEcoAccountForm(request.POST or None)
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
if request.method == "POST":
@ -93,7 +93,6 @@ def new_view(request: HttpRequest):
context = {
"form": data_form,
"geom_form": geom_form,
"url": reverse("compensation:acc-new-id")
}
context = BaseContext(request, context).context
return render(request, template, context)
@ -129,7 +128,7 @@ def edit_view(request: HttpRequest, id: str):
Returns:
"""
template = "compensation/new/view.html"
template = "compensation/form/view.html"
# Get object from db
acc = get_object_or_404(EcoAccount, id=id)
# Create forms, initialize with values from db/from POST request

159
ema/forms.py Normal file
View File

@ -0,0 +1,159 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 06.10.21
"""
from django.contrib.auth.models import User
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 ema.models import Ema
from intervention.models import ResponsibilityData
from konova.forms import SimpleGeomForm
from user.models import UserActionLogEntry, UserAction
class NewEmaForm(AbstractCompensationForm, CompensationResponsibleFormMixin):
""" Form for creating new EMA objects.
Inherits basic form fields from AbstractCompensationForm and additional from CompensationResponsibleFormMixin.
Second holds self.instance.response related fields
"""
field_order = [
"identifier",
"title",
"conservation_office",
"conservation_file_number",
"handler",
"fundings",
"comment",
]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.form_title = _("New EMA")
self.action_url = reverse("ema:new")
self.cancel_redirect = reverse("ema:index")
tmp = Ema()
identifier = tmp.generate_new_identifier()
self.initialize_form_field("identifier", identifier)
self.fields["identifier"].widget.attrs["url"] = reverse_lazy("ema:new-id")
self.fields["title"].widget.attrs["placeholder"] = _("Compensation XY; Location ABC")
def save(self, user: User, geom_form: SimpleGeomForm):
with transaction.atomic():
# Fetch data from cleaned POST values
identifier = self.cleaned_data.get("identifier", None)
title = self.cleaned_data.get("title", None)
fundings = self.cleaned_data.get("fundings", None)
handler = self.cleaned_data.get("handler", 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)
# Create log entry
action = UserActionLogEntry.objects.create(
user=user,
action=UserAction.CREATED,
)
# Process the geometry form
geometry = geom_form.save(action)
responsible = ResponsibilityData.objects.create(
handler=handler,
conservation_file_number=conservation_file_number,
conservation_office=conservation_office,
)
# Finally create main object
acc = Ema.objects.create(
identifier=identifier,
title=title,
responsible=responsible,
created=action,
geometry=geometry,
comment=comment,
)
acc.fundings.set(fundings)
# Add the creating user to the list of shared users
acc.users.add(user)
# Add the log entry to the main objects log list
acc.log.add(action)
return acc
class EditEmaForm(NewEmaForm):
""" Form for editing EMAs
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.form_title = _("Edit EMA")
self.action_url = reverse("ema:edit", args=(self.instance.id,))
self.cancel_redirect = reverse("ema:open", args=(self.instance.id,))
self.fields["identifier"].widget.attrs["url"] = reverse_lazy("ema:new-id")
self.fields["title"].widget.attrs["placeholder"] = _("Compensation XY; Location ABC")
# Initialize form data
form_data = {
"identifier": self.instance.identifier,
"title": self.instance.title,
"handler": self.instance.responsible.handler,
"conservation_office": self.instance.responsible.conservation_office,
"conservation_file_number": self.instance.responsible.conservation_file_number,
"fundings": self.instance.fundings.all(),
"comment": self.instance.comment,
}
disabled_fields = []
self.load_initial_data(
form_data,
disabled_fields
)
def save(self, user: User, geom_form: SimpleGeomForm):
with transaction.atomic():
# Fetch data from cleaned POST values
identifier = self.cleaned_data.get("identifier", None)
title = self.cleaned_data.get("title", None)
fundings = self.cleaned_data.get("fundings", None)
handler = self.cleaned_data.get("handler", 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)
# Create log entry
action = UserActionLogEntry.objects.create(
user=user,
action=UserAction.EDITED,
)
# Process the geometry form
geometry = geom_form.save(action)
# Update responsible data
self.instance.responsible.handler = handler
self.instance.responsible.conservation_office = conservation_office
self.instance.responsible.conservation_file_number = conservation_file_number
self.instance.responsible.save()
# Update main oject data
self.instance.identifier = identifier
self.instance.title = title
self.instance.geometry = geometry
self.instance.comment = comment
self.instance.modified = action
self.instance.save()
self.instance.fundings.set(fundings)
# Add the log entry to the main objects log list
self.instance.log.add(action)
return self.instance

View File

@ -24,7 +24,7 @@
{% endif %}
{% endif %}
{% if is_default_member %}
<a href="{% url 'home' %}" class="mr-2">
<a href="{% url 'ema:edit' obj.id %}" class="mr-2">
<button class="btn btn-default" title="{% trans 'Edit' %}">
{% fa5_icon 'edit' %}
</button>

View File

@ -12,6 +12,7 @@ app_name = "ema"
urlpatterns = [
path("", index_view, name="index"),
path("new/", new_view, name="new"),
path("new/id", new_id_view, name="new-id"),
path("<id>", open_view, name="open"),
path('<id>/log', log_view, name='log'),
path('<id>/edit', edit_view, name='edit'),

View File

@ -1,12 +1,14 @@
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.db.models import Sum
from django.http import HttpRequest
from django.shortcuts import render, get_object_or_404
from django.http import HttpRequest, JsonResponse
from django.shortcuts import render, get_object_or_404, redirect
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
import compensation
from compensation.forms.modalForms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm
from ema.forms import NewEmaForm, EditEmaForm
from ema.tables import EmaTable
from konova.contexts import BaseContext
from konova.decorators import conservation_office_group_required
@ -14,6 +16,7 @@ from ema.models import Ema, EmaDocument
from konova.forms import RemoveModalForm, NewDocumentForm, SimpleGeomForm, RecordModalForm
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
from konova.utils.documents import get_document, remove_document
from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID
from konova.utils.user_checks import in_group
@ -47,7 +50,8 @@ def index_view(request: HttpRequest):
@login_required
@conservation_office_group_required
def new_view(request: HttpRequest):
""" Renders the form for a new EMA
"""
Renders a view for a new eco account creation
Args:
request (HttpRequest): The incoming request
@ -55,12 +59,54 @@ def new_view(request: HttpRequest):
Returns:
"""
template = "generic_index.html"
context = {}
template = "ema/form/view.html"
data_form = NewEmaForm(request.POST or None)
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
if request.method == "POST":
if data_form.is_valid() and geom_form.is_valid():
generated_identifier = data_form.cleaned_data.get("identifier", None)
ema = data_form.save(request.user, geom_form)
if generated_identifier != ema.identifier:
messages.info(
request,
IDENTIFIER_REPLACED.format(
generated_identifier,
ema.identifier
)
)
messages.success(request, _("EMA {} added").format(ema.identifier))
return redirect("ema:open", id=ema.id)
else:
messages.error(request, FORM_INVALID)
else:
# For clarification: nothing in this case
pass
context = {
"form": data_form,
"geom_form": geom_form,
}
context = BaseContext(request, context).context
return render(request, template, context)
@login_required
def new_id_view(request: HttpRequest):
""" JSON endpoint
Provides fetching of free identifiers for e.g. AJAX calls
"""
tmp = Ema()
identifier = tmp.generate_new_identifier()
while Ema.objects.filter(identifier=identifier).exists():
identifier = tmp.generate_new_identifier()
return JsonResponse(
data={
"identifier": identifier
}
)
@login_required
def open_view(request: HttpRequest, id: str):
""" Renders the detail view of an EMA
@ -133,7 +179,38 @@ def log_view(request: HttpRequest, id: str):
@login_required
def edit_view(request: HttpRequest, id: str):
get_object_or_404(Ema, id=id)
"""
Renders a view for editing compensations
Args:
request (HttpRequest): The incoming request
Returns:
"""
template = "compensation/form/view.html"
# Get object from db
ema = get_object_or_404(Ema, 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)
if request.method == "POST":
if data_form.is_valid() and geom_form.is_valid():
# The data form takes the geom form for processing, as well as the performing user
ema = data_form.save(request.user, geom_form)
messages.success(request, _("EMA {} edited").format(ema.identifier))
return redirect("ema:open", id=ema.id)
else:
messages.error(request, FORM_INVALID)
else:
# For clarification: nothing in this case
pass
context = {
"form": data_form,
"geom_form": geom_form,
}
context = BaseContext(request, context).context
return render(request, template, context)
@login_required

View File

@ -0,0 +1,6 @@
{% extends 'base.html' %}
{% load i18n l10n %}
{% block body %}
{% include 'form/main_data_collapse_form.html' %}
{% endblock %}

View File

@ -58,7 +58,7 @@ def new_view(request: HttpRequest):
Returns:
"""
template = "intervention/new/view.html"
template = "intervention/form/view.html"
data_form = NewInterventionForm(request.POST or None)
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
if request.method == "POST":
@ -83,7 +83,6 @@ def new_view(request: HttpRequest):
context = {
"form": data_form,
"geom_form": geom_form,
"url": reverse("intervention:new-id")
}
context = BaseContext(request, context).context
return render(request, template, context)
@ -242,7 +241,7 @@ def edit_view(request: HttpRequest, id: str):
Returns:
"""
template = "intervention/new/view.html"
template = "intervention/form/view.html"
# Get object from db
intervention = get_object_or_404(Intervention, id=id)
# Create forms, initialize with values from db/from POST request