#7 New forms WIP
* adds saving functionality for new intervention form * refactors new identifier generating, so a pre-generated identifier from a new element form will be checked again before saving * adds css fixes for disturbing input field:focus bugs * adds missing csrf token to new collapsible form * adds/updates translations * introduces mark_as_deleted as only marking instead of using delete() which will really delete from the db
This commit is contained in:
@@ -8,23 +8,21 @@ Created on: 02.12.20
|
||||
from dal import autocomplete
|
||||
from django import forms
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.gis import forms as gis_forms
|
||||
from django.contrib.gis.geos import Polygon
|
||||
from django.db import transaction
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from codelist.models import KonovaCode
|
||||
from codelist.settings import CODELIST_BIOTOPES_ID, CODELIST_PROCESS_TYPE_ID, CODELIST_LAW_ID, \
|
||||
from codelist.settings import CODELIST_PROCESS_TYPE_ID, CODELIST_LAW_ID, \
|
||||
CODELIST_REGISTRATION_OFFICE_ID, CODELIST_CONSERVATION_OFFICE_ID
|
||||
from compensation.models import EcoAccountDeduction, EcoAccount
|
||||
from intervention.models import Intervention, Revocation, RevocationDocument
|
||||
from konova.forms import BaseForm, BaseModalForm
|
||||
from konova.settings import DEFAULT_LAT, DEFAULT_LON, DEFAULT_ZOOM, ZB_GROUP, ETS_GROUP
|
||||
from intervention.models import Intervention, Revocation, RevocationDocument, LegalData, ResponsibilityData
|
||||
from konova.forms import BaseForm, BaseModalForm, SimpleGeomForm
|
||||
from konova.settings import ZB_GROUP, ETS_GROUP
|
||||
from konova.utils.general import format_german_float
|
||||
from konova.utils.messenger import Messenger
|
||||
from konova.utils.user_checks import in_group
|
||||
from organisation.models import Organisation
|
||||
from user.models import UserActionLogEntry, UserAction
|
||||
|
||||
|
||||
@@ -40,10 +38,16 @@ class NewInterventionForm(BaseForm):
|
||||
label_suffix="",
|
||||
help_text=_("An explanatory name"),
|
||||
max_length=255,
|
||||
widget=forms.TextInput(
|
||||
attrs={
|
||||
"placeholder": _("Construction XY; Location ABC")
|
||||
}
|
||||
)
|
||||
)
|
||||
type = forms.ModelChoiceField(
|
||||
label=_("Process type"),
|
||||
label_suffix="",
|
||||
help_text=_(""),
|
||||
required=False,
|
||||
queryset=KonovaCode.objects.filter(
|
||||
is_archived=False,
|
||||
@@ -107,12 +111,22 @@ class NewInterventionForm(BaseForm):
|
||||
label_suffix="",
|
||||
max_length=255,
|
||||
required=False,
|
||||
widget=forms.TextInput(
|
||||
attrs={
|
||||
"placeholder": _("ZB-123/ABC.456")
|
||||
}
|
||||
)
|
||||
)
|
||||
conservation_office_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")
|
||||
}
|
||||
)
|
||||
)
|
||||
handler = forms.CharField(
|
||||
label=_("Intervention handler"),
|
||||
@@ -120,20 +134,45 @@ class NewInterventionForm(BaseForm):
|
||||
max_length=255,
|
||||
required=False,
|
||||
help_text=_("Who performs the intervention"),
|
||||
)
|
||||
geometry = gis_forms.MultiPolygonField(
|
||||
widget=gis_forms.OSMWidget(
|
||||
widget=forms.TextInput(
|
||||
attrs={
|
||||
"default_lat": DEFAULT_LAT,
|
||||
"default_lon": DEFAULT_LON,
|
||||
"default_zoom": DEFAULT_ZOOM,
|
||||
'map_width': 800,
|
||||
'map_height': 500
|
||||
"placeholder": _("Company Mustermann")
|
||||
}
|
||||
)
|
||||
)
|
||||
registration_date = forms.DateField(
|
||||
label=_("Registration date"),
|
||||
label_suffix=_(""),
|
||||
required=False,
|
||||
widget=forms.DateInput(
|
||||
attrs={
|
||||
"type": "date",
|
||||
},
|
||||
),
|
||||
label=_("Map"),
|
||||
format="%d.%m.%Y"
|
||||
)
|
||||
)
|
||||
binding_date = forms.DateField(
|
||||
label=_("Binding on"),
|
||||
label_suffix=_(""),
|
||||
required=False,
|
||||
widget=forms.DateInput(
|
||||
attrs={
|
||||
"type": "date",
|
||||
},
|
||||
format="%d.%m.%Y"
|
||||
)
|
||||
)
|
||||
comment = forms.CharField(
|
||||
label_suffix="",
|
||||
help_text=_("Where does the intervention take place")
|
||||
label=_("Comment"),
|
||||
required=False,
|
||||
help_text=_("Additional comment"),
|
||||
widget=forms.Textarea(
|
||||
attrs={
|
||||
"rows": 5,
|
||||
"class": "w-100"
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -142,30 +181,69 @@ class NewInterventionForm(BaseForm):
|
||||
self.action_url = reverse("intervention:new")
|
||||
self.cancel_redirect = reverse("intervention:index")
|
||||
|
||||
def save(self, user: User):
|
||||
tmp_intervention = Intervention()
|
||||
identifier = tmp_intervention._generate_new_identifier()
|
||||
self.initialize_form_field("identifier", identifier)
|
||||
|
||||
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)
|
||||
_type = self.cleaned_data.get("type", None)
|
||||
laws = self.cleaned_data.get("laws", None)
|
||||
handler = self.cleaned_data.get("handler", None)
|
||||
geometry = self.cleaned_data.get("geometry", Polygon())
|
||||
registration_office = self.cleaned_data.get("registration_office", None)
|
||||
conservation_office = self.cleaned_data.get("conservation_office", None)
|
||||
conservation_office_file_number = self.cleaned_data.get("conservation_office_file_number", None)
|
||||
registration_office_file_number = self.cleaned_data.get("registration_office_file_number", None)
|
||||
binding_date = self.cleaned_data.get("binding_date", None)
|
||||
registration_date = self.cleaned_data.get("registration_date", None)
|
||||
comment = self.cleaned_data.get("comment", None)
|
||||
|
||||
# Create log entry
|
||||
action = UserActionLogEntry.objects.create(
|
||||
user=user,
|
||||
action=UserAction.CREATED,
|
||||
)
|
||||
intervention = Intervention(
|
||||
|
||||
# Create legal data object (without M2M laws first)
|
||||
legal_data = LegalData.objects.create(
|
||||
registration_date=registration_date,
|
||||
binding_date=binding_date,
|
||||
process_type=_type,
|
||||
)
|
||||
# Then add the M2M laws to the object
|
||||
legal_data.laws.set(laws)
|
||||
|
||||
# Create responsible data object
|
||||
responsibility_data = ResponsibilityData.objects.create(
|
||||
registration_office=registration_office,
|
||||
conservation_office=conservation_office,
|
||||
registration_file_number=registration_office_file_number,
|
||||
conservation_file_number=conservation_office_file_number,
|
||||
handler=handler,
|
||||
)
|
||||
|
||||
# Process the geometry form
|
||||
geometry = geom_form.save(action)
|
||||
|
||||
# Finally create main object, holding the other objects
|
||||
intervention = Intervention.objects.create(
|
||||
identifier=identifier,
|
||||
title=title,
|
||||
type=_type,
|
||||
laws=laws,
|
||||
handler=handler,
|
||||
geometry=geometry,
|
||||
responsible=responsibility_data,
|
||||
legal=legal_data,
|
||||
created=action,
|
||||
geometry=geometry,
|
||||
comment=comment,
|
||||
)
|
||||
intervention.save()
|
||||
|
||||
# Add the log entry to the main objects log list
|
||||
intervention.log.add(action)
|
||||
|
||||
# Add the performing user as the first user having access to the data
|
||||
intervention.users.add(user)
|
||||
return intervention
|
||||
|
||||
|
||||
@@ -384,6 +462,9 @@ class NewRevocationForm(BaseModalForm):
|
||||
)
|
||||
)
|
||||
|
||||
# Define w-100 for all form fields
|
||||
full_width_fields = True
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.form_title = _("Add revocation")
|
||||
@@ -532,6 +613,9 @@ class NewDeductionForm(BaseModalForm):
|
||||
),
|
||||
)
|
||||
|
||||
# Define w-100 for all form fields
|
||||
full_width_fields = True
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.form_title = _("New Deduction")
|
||||
|
||||
@@ -260,14 +260,41 @@ class Intervention(BaseObject):
|
||||
self.save()
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
""" Custom save functionality
|
||||
|
||||
Performs some pre-save checks:
|
||||
1. Checking for existing identifiers
|
||||
|
||||
Args:
|
||||
*args ():
|
||||
**kwargs ():
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
if self.identifier is None or len(self.identifier) == 0:
|
||||
# Create new identifier
|
||||
new_id = self._generate_new_identifier()
|
||||
while Intervention.objects.filter(identifier=new_id).exists():
|
||||
new_id = self._generate_new_identifier()
|
||||
self.identifier = new_id
|
||||
# No identifier given
|
||||
self.identifier = self._generate_new_identifier()
|
||||
|
||||
# Before saving, make sure the set identifier is not used, yet
|
||||
while Intervention.objects.filter(identifier=self.identifier).exists():
|
||||
self.identifier = self._generate_new_identifier()
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def delete(self, using=None, keep_parents=False):
|
||||
to_delete = [
|
||||
self.legal,
|
||||
self.responsible,
|
||||
self.geometry,
|
||||
self.log.all()
|
||||
]
|
||||
for entry in to_delete:
|
||||
try:
|
||||
entry.delete()
|
||||
except AttributeError:
|
||||
pass
|
||||
super().delete(using, keep_parents)
|
||||
|
||||
def quality_check(self) -> list:
|
||||
""" Quality check
|
||||
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n l10n %}
|
||||
|
||||
{% block head %}
|
||||
{% comment %}
|
||||
dal documentation (django-autocomplete-light) states using form.media for adding needed scripts.
|
||||
This does not work properly with modal forms, as the scripts are not loaded properly inside the modal.
|
||||
Therefore the script linkages from form.media have been extracted and put inside dal/scripts.html to ensure
|
||||
these scripts are loaded when needed.
|
||||
{% endcomment %}
|
||||
{% include 'dal/scripts.html' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<h2>{{data_form.form_title}}</h2>
|
||||
{% include 'form/main_data_collapse_form.html' %}
|
||||
{% endblock %}
|
||||
@@ -59,11 +59,18 @@ def new_view(request: HttpRequest):
|
||||
|
||||
"""
|
||||
template = "intervention/new/view.html"
|
||||
form = NewInterventionForm(request.POST or None)
|
||||
data_form = NewInterventionForm(request.POST or None)
|
||||
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
|
||||
if request.method == "POST":
|
||||
if form.is_valid():
|
||||
intervention = form.save(request.user)
|
||||
messages.success(request, _("Intervention {} added").format(intervention.title))
|
||||
if data_form.is_valid() and geom_form.is_valid():
|
||||
generated_identifier = data_form.cleaned_data.get("identifier", None)
|
||||
intervention = data_form.save(request.user, geom_form)
|
||||
if generated_identifier != intervention.identifier:
|
||||
messages.info(
|
||||
request,
|
||||
_("The identifier '{}' had to be changed to '{}' since another entry has been added in the meanwhile, which uses this identifier")
|
||||
)
|
||||
messages.success(request, _("Intervention {} added").format(intervention.identifier))
|
||||
return redirect("intervention:index")
|
||||
else:
|
||||
messages.error(request, _("Invalid input"))
|
||||
@@ -71,7 +78,8 @@ def new_view(request: HttpRequest):
|
||||
# For clarification: nothing in this case
|
||||
pass
|
||||
context = {
|
||||
"form": form,
|
||||
"data_form": data_form,
|
||||
"geom_form": geom_form,
|
||||
}
|
||||
context = BaseContext(request, context).context
|
||||
return render(request, template, context)
|
||||
|
||||
Reference in New Issue
Block a user