* 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 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): class NewCompensationForm(AbstractCompensationForm):
""" Form for creating new compensations. """ Form for creating new compensations.
Can be initialized with an intervention id for preselecting the related intervention. 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( intervention = forms.ModelChoiceField(
@ -172,6 +223,9 @@ class NewCompensationForm(AbstractCompensationForm):
class EditCompensationForm(NewCompensationForm): class EditCompensationForm(NewCompensationForm):
""" Form for editing compensations
"""
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.form_title = _("Edit compensation") self.form_title = _("Edit compensation")
@ -224,48 +278,12 @@ class EditCompensationForm(NewCompensationForm):
return self.instance return self.instance
class NewEcoAccountForm(AbstractCompensationForm): class NewEcoAccountForm(AbstractCompensationForm, CompensationResponsibleFormMixin):
conservation_office = forms.ModelChoiceField( """ Form for creating eco accounts
label=_("Conservation office"),
label_suffix="", Inherits from basic AbstractCompensationForm and further form fields from CompensationResponsibleFormMixin
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",
}
)
)
field_order = [ field_order = [
"identifier", "identifier",
"title", "title",
@ -333,6 +351,9 @@ class NewEcoAccountForm(AbstractCompensationForm):
class EditEcoAccountForm(NewEcoAccountForm): class EditEcoAccountForm(NewEcoAccountForm):
""" Form for editing eco accounts
"""
surface = forms.DecimalField( surface = forms.DecimalField(
min_value=0.00, min_value=0.00,
decimal_places=2, decimal_places=2,

View File

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

View File

@ -68,7 +68,7 @@ def new_view(request: HttpRequest):
Returns: Returns:
""" """
template = "compensation/new/view.html" template = "compensation/form/view.html"
data_form = NewEcoAccountForm(request.POST or None) data_form = NewEcoAccountForm(request.POST or None)
geom_form = SimpleGeomForm(request.POST or None, read_only=False) geom_form = SimpleGeomForm(request.POST or None, read_only=False)
if request.method == "POST": if request.method == "POST":
@ -93,7 +93,6 @@ def new_view(request: HttpRequest):
context = { context = {
"form": data_form, "form": data_form,
"geom_form": geom_form, "geom_form": geom_form,
"url": reverse("compensation:acc-new-id")
} }
context = BaseContext(request, context).context context = BaseContext(request, context).context
return render(request, template, context) return render(request, template, context)
@ -129,7 +128,7 @@ def edit_view(request: HttpRequest, id: str):
Returns: Returns:
""" """
template = "compensation/new/view.html" template = "compensation/form/view.html"
# Get object from db # Get object from db
acc = get_object_or_404(EcoAccount, id=id) acc = get_object_or_404(EcoAccount, id=id)
# Create forms, initialize with values from db/from POST request # 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 %}
{% endif %} {% endif %}
{% if is_default_member %} {% 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' %}"> <button class="btn btn-default" title="{% trans 'Edit' %}">
{% fa5_icon 'edit' %} {% fa5_icon 'edit' %}
</button> </button>

View File

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

View File

@ -1,12 +1,14 @@
from django.contrib import messages
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.db.models import Sum from django.db.models import Sum
from django.http import HttpRequest from django.http import HttpRequest, JsonResponse
from django.shortcuts import render, get_object_or_404 from django.shortcuts import render, get_object_or_404, redirect
from django.urls import reverse from django.urls import reverse
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
import compensation import compensation
from compensation.forms.modalForms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm from compensation.forms.modalForms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm
from ema.forms import NewEmaForm, EditEmaForm
from ema.tables import EmaTable from ema.tables import EmaTable
from konova.contexts import BaseContext from konova.contexts import BaseContext
from konova.decorators import conservation_office_group_required 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.forms import RemoveModalForm, NewDocumentForm, SimpleGeomForm, RecordModalForm
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
from konova.utils.documents import get_document, remove_document from konova.utils.documents import get_document, remove_document
from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID
from konova.utils.user_checks import in_group from konova.utils.user_checks import in_group
@ -47,7 +50,8 @@ def index_view(request: HttpRequest):
@login_required @login_required
@conservation_office_group_required @conservation_office_group_required
def new_view(request: HttpRequest): def new_view(request: HttpRequest):
""" Renders the form for a new EMA """
Renders a view for a new eco account creation
Args: Args:
request (HttpRequest): The incoming request request (HttpRequest): The incoming request
@ -55,12 +59,54 @@ def new_view(request: HttpRequest):
Returns: Returns:
""" """
template = "generic_index.html" template = "ema/form/view.html"
context = {} 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 context = BaseContext(request, context).context
return render(request, template, 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 @login_required
def open_view(request: HttpRequest, id: str): def open_view(request: HttpRequest, id: str):
""" Renders the detail view of an EMA """ Renders the detail view of an EMA
@ -133,7 +179,38 @@ def log_view(request: HttpRequest, id: str):
@login_required @login_required
def edit_view(request: HttpRequest, id: str): 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 @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: Returns:
""" """
template = "intervention/new/view.html" template = "intervention/form/view.html"
data_form = NewInterventionForm(request.POST or None) data_form = NewInterventionForm(request.POST or None)
geom_form = SimpleGeomForm(request.POST or None, read_only=False) geom_form = SimpleGeomForm(request.POST or None, read_only=False)
if request.method == "POST": if request.method == "POST":
@ -83,7 +83,6 @@ def new_view(request: HttpRequest):
context = { context = {
"form": data_form, "form": data_form,
"geom_form": geom_form, "geom_form": geom_form,
"url": reverse("intervention:new-id")
} }
context = BaseContext(request, context).context context = BaseContext(request, context).context
return render(request, template, context) return render(request, template, context)
@ -242,7 +241,7 @@ def edit_view(request: HttpRequest, id: str):
Returns: Returns:
""" """
template = "intervention/new/view.html" template = "intervention/form/view.html"
# Get object from db # Get object from db
intervention = get_object_or_404(Intervention, id=id) intervention = get_object_or_404(Intervention, id=id)
# Create forms, initialize with values from db/from POST request # Create forms, initialize with values from db/from POST request