Merge branch 'master' into 132_Old_entries
# Conflicts: # konova/models/geometry.py
This commit is contained in:
commit
4f5c172449
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"eco_account": "CHANGE_BEFORE_RUN!!!",
|
"eco_account": "CHANGE_BEFORE_RUN!!!",
|
||||||
"surface": 500.0,
|
"surface": 500.50,
|
||||||
"intervention": "CHANGE_BEFORE_RUN!!!"
|
"intervention": "CHANGE_BEFORE_RUN!!!"
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"eco_account": "CHANGE_BEFORE_RUN!!!",
|
"eco_account": "CHANGE_BEFORE_RUN!!!",
|
||||||
"surface": 523400.0,
|
"surface": 523400.50,
|
||||||
"intervention": "CHANGE_BEFORE_RUN!!!"
|
"intervention": "CHANGE_BEFORE_RUN!!!"
|
||||||
}
|
}
|
@ -136,8 +136,6 @@ class AbstractModelAPISerializer:
|
|||||||
geometry = geos.fromstr(geojson)
|
geometry = geos.fromstr(geojson)
|
||||||
if geometry.srid != DEFAULT_SRID_RLP:
|
if geometry.srid != DEFAULT_SRID_RLP:
|
||||||
geometry.transform(DEFAULT_SRID_RLP)
|
geometry.transform(DEFAULT_SRID_RLP)
|
||||||
if geometry.empty:
|
|
||||||
geometry = None
|
|
||||||
return geometry
|
return geometry
|
||||||
|
|
||||||
def _get_obj_from_db(self, id, user):
|
def _get_obj_from_db(self, id, user):
|
||||||
|
@ -11,7 +11,7 @@ from api.utils.serializer.v1.serializer import AbstractModelAPISerializerV1, Abs
|
|||||||
from compensation.models import Compensation
|
from compensation.models import Compensation
|
||||||
from intervention.models import Intervention
|
from intervention.models import Intervention
|
||||||
from konova.models import Geometry
|
from konova.models import Geometry
|
||||||
from konova.tasks import celery_update_parcels
|
from konova.tasks import celery_update_parcels, celery_check_for_geometry_conflicts
|
||||||
from konova.utils.message_templates import DATA_UNSHARED
|
from konova.utils.message_templates import DATA_UNSHARED
|
||||||
from user.models import UserActionLogEntry
|
from user.models import UserActionLogEntry
|
||||||
|
|
||||||
@ -128,6 +128,7 @@ class CompensationAPISerializerV1(AbstractModelAPISerializerV1, AbstractCompensa
|
|||||||
obj.log.add(obj.created)
|
obj.log.add(obj.created)
|
||||||
|
|
||||||
celery_update_parcels.delay(obj.geometry.id)
|
celery_update_parcels.delay(obj.geometry.id)
|
||||||
|
celery_check_for_geometry_conflicts.delay(obj.geometry.id)
|
||||||
|
|
||||||
return obj.id
|
return obj.id
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID, CODELIST_HANDLER_
|
|||||||
from compensation.models import EcoAccount
|
from compensation.models import EcoAccount
|
||||||
from intervention.models import Legal, Responsibility, Handler
|
from intervention.models import Legal, Responsibility, Handler
|
||||||
from konova.models import Geometry
|
from konova.models import Geometry
|
||||||
from konova.tasks import celery_update_parcels
|
from konova.tasks import celery_update_parcels, celery_check_for_geometry_conflicts
|
||||||
from user.models import UserActionLogEntry
|
from user.models import UserActionLogEntry
|
||||||
|
|
||||||
|
|
||||||
@ -150,6 +150,7 @@ class EcoAccountAPISerializerV1(AbstractModelAPISerializerV1,
|
|||||||
obj.users.add(user)
|
obj.users.add(user)
|
||||||
|
|
||||||
celery_update_parcels.delay(obj.geometry.id)
|
celery_update_parcels.delay(obj.geometry.id)
|
||||||
|
celery_check_for_geometry_conflicts.delay(obj.geometry.id)
|
||||||
|
|
||||||
return obj.id
|
return obj.id
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID, CODELIST_HANDLER_
|
|||||||
from ema.models import Ema
|
from ema.models import Ema
|
||||||
from intervention.models import Responsibility, Handler
|
from intervention.models import Responsibility, Handler
|
||||||
from konova.models import Geometry
|
from konova.models import Geometry
|
||||||
from konova.tasks import celery_update_parcels
|
from konova.tasks import celery_update_parcels, celery_check_for_geometry_conflicts
|
||||||
from user.models import UserActionLogEntry
|
from user.models import UserActionLogEntry
|
||||||
|
|
||||||
|
|
||||||
@ -122,6 +122,7 @@ class EmaAPISerializerV1(AbstractModelAPISerializerV1, AbstractCompensationAPISe
|
|||||||
obj.users.add(user)
|
obj.users.add(user)
|
||||||
|
|
||||||
celery_update_parcels.delay(obj.geometry.id)
|
celery_update_parcels.delay(obj.geometry.id)
|
||||||
|
celery_check_for_geometry_conflicts.delay(obj.geometry.id)
|
||||||
|
|
||||||
return obj.id
|
return obj.id
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ from api.utils.serializer.v1.serializer import AbstractModelAPISerializerV1, \
|
|||||||
from compensation.models import Payment
|
from compensation.models import Payment
|
||||||
from intervention.models import Intervention, Responsibility, Legal, Handler
|
from intervention.models import Intervention, Responsibility, Legal, Handler
|
||||||
from konova.models import Geometry
|
from konova.models import Geometry
|
||||||
from konova.tasks import celery_update_parcels
|
from konova.tasks import celery_update_parcels, celery_check_for_geometry_conflicts
|
||||||
from user.models import UserActionLogEntry
|
from user.models import UserActionLogEntry
|
||||||
|
|
||||||
|
|
||||||
@ -165,6 +165,7 @@ class InterventionAPISerializerV1(AbstractModelAPISerializerV1,
|
|||||||
obj.log.add(obj.created)
|
obj.log.add(obj.created)
|
||||||
|
|
||||||
celery_update_parcels.delay(obj.geometry.id)
|
celery_update_parcels.delay(obj.geometry.id)
|
||||||
|
celery_check_for_geometry_conflicts.delay(obj.geometry.id)
|
||||||
|
|
||||||
return obj.id
|
return obj.id
|
||||||
|
|
||||||
|
@ -392,7 +392,8 @@ class AbstractCompensationAPISerializerV1Mixin:
|
|||||||
self._konova_code_from_json(e, CODELIST_COMPENSATION_ACTION_DETAIL_ID) for e in entry["action_details"]
|
self._konova_code_from_json(e, CODELIST_COMPENSATION_ACTION_DETAIL_ID) for e in entry["action_details"]
|
||||||
]
|
]
|
||||||
amount = float(entry["amount"])
|
amount = float(entry["amount"])
|
||||||
unit = entry["unit"]
|
# Mapping of old "qm" into "m²"
|
||||||
|
unit = UnitChoices.m2.value if entry["unit"] == "qm" else entry["unit"]
|
||||||
comment = entry["comment"]
|
comment = entry["comment"]
|
||||||
|
|
||||||
# Check on validity
|
# Check on validity
|
||||||
|
@ -129,12 +129,11 @@ class NewCompensationForm(AbstractCompensationForm,
|
|||||||
self.initialize_form_field("identifier", identifier)
|
self.initialize_form_field("identifier", identifier)
|
||||||
self.fields["identifier"].widget.attrs["url"] = reverse_lazy("compensation:new-id")
|
self.fields["identifier"].widget.attrs["url"] = reverse_lazy("compensation:new-id")
|
||||||
|
|
||||||
def __create_comp(self, user, geom_form) -> Compensation:
|
def __create_comp(self, user):
|
||||||
""" Creates the compensation from form data
|
""" Creates the compensation from form data
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
user (User): The performing user
|
user (User): The performing user
|
||||||
geom_form (SimpleGeomForm): The geometry form
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
comp (Compensation): The compensation object
|
comp (Compensation): The compensation object
|
||||||
@ -150,8 +149,6 @@ class NewCompensationForm(AbstractCompensationForm,
|
|||||||
|
|
||||||
# Create log entry
|
# Create log entry
|
||||||
action = UserActionLogEntry.get_created_action(user)
|
action = UserActionLogEntry.get_created_action(user)
|
||||||
# Process the geometry form
|
|
||||||
geometry = geom_form.save(action)
|
|
||||||
|
|
||||||
# Finally create main object
|
# Finally create main object
|
||||||
comp = Compensation.objects.create(
|
comp = Compensation.objects.create(
|
||||||
@ -162,18 +159,23 @@ class NewCompensationForm(AbstractCompensationForm,
|
|||||||
is_cef=is_cef,
|
is_cef=is_cef,
|
||||||
is_coherence_keeping=is_coherence_keeping,
|
is_coherence_keeping=is_coherence_keeping,
|
||||||
is_pik=is_pik,
|
is_pik=is_pik,
|
||||||
geometry=geometry,
|
|
||||||
comment=comment,
|
comment=comment,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add the log entry to the main objects log list
|
# Add the log entry to the main objects log list
|
||||||
comp.log.add(action)
|
comp.log.add(action)
|
||||||
return comp
|
return comp, action
|
||||||
|
|
||||||
def save(self, user: User, geom_form: SimpleGeomForm):
|
def save(self, user: User, geom_form: SimpleGeomForm):
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
comp = self.__create_comp(user, geom_form)
|
comp, action = self.__create_comp(user)
|
||||||
comp.intervention.mark_as_edited(user, edit_comment=COMPENSATION_ADDED_TEMPLATE.format(comp.identifier))
|
comp.intervention.mark_as_edited(user, edit_comment=COMPENSATION_ADDED_TEMPLATE.format(comp.identifier))
|
||||||
|
|
||||||
|
# Process the geometry form
|
||||||
|
geometry = geom_form.save(action)
|
||||||
|
comp.geometry = geometry
|
||||||
|
comp.save()
|
||||||
|
|
||||||
return comp
|
return comp
|
||||||
|
|
||||||
|
|
||||||
@ -205,6 +207,9 @@ class EditCompensationForm(NewCompensationForm):
|
|||||||
|
|
||||||
def save(self, user: User, geom_form: SimpleGeomForm):
|
def save(self, user: User, geom_form: SimpleGeomForm):
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
|
# Create log entry
|
||||||
|
action = UserActionLogEntry.get_edited_action(user)
|
||||||
|
|
||||||
# Fetch data from cleaned POST values
|
# Fetch data from cleaned POST values
|
||||||
identifier = self.cleaned_data.get("identifier", None)
|
identifier = self.cleaned_data.get("identifier", None)
|
||||||
title = self.cleaned_data.get("title", None)
|
title = self.cleaned_data.get("title", None)
|
||||||
@ -214,17 +219,9 @@ class EditCompensationForm(NewCompensationForm):
|
|||||||
is_pik = self.cleaned_data.get("is_pik", None)
|
is_pik = self.cleaned_data.get("is_pik", None)
|
||||||
comment = self.cleaned_data.get("comment", None)
|
comment = self.cleaned_data.get("comment", None)
|
||||||
|
|
||||||
# Create log entry
|
|
||||||
action = UserActionLogEntry.get_edited_action(user)
|
|
||||||
|
|
||||||
# Process the geometry form
|
|
||||||
geometry = geom_form.save(action)
|
|
||||||
|
|
||||||
# Finally create main object
|
|
||||||
self.instance.identifier = identifier
|
self.instance.identifier = identifier
|
||||||
self.instance.title = title
|
self.instance.title = title
|
||||||
self.instance.intervention = intervention
|
self.instance.intervention = intervention
|
||||||
self.instance.geometry = geometry
|
|
||||||
self.instance.is_cef = is_cef
|
self.instance.is_cef = is_cef
|
||||||
self.instance.is_coherence_keeping = is_coherence_keeping
|
self.instance.is_coherence_keeping = is_coherence_keeping
|
||||||
self.instance.comment = comment
|
self.instance.comment = comment
|
||||||
@ -233,6 +230,11 @@ class EditCompensationForm(NewCompensationForm):
|
|||||||
self.instance.save()
|
self.instance.save()
|
||||||
|
|
||||||
self.instance.log.add(action)
|
self.instance.log.add(action)
|
||||||
|
|
||||||
intervention.mark_as_edited(user, self.request, EDITED_GENERAL_DATA)
|
intervention.mark_as_edited(user, self.request, EDITED_GENERAL_DATA)
|
||||||
return self.instance
|
|
||||||
|
# Process the geometry form (NOT ATOMIC TRANSACTION DUE TO CELERY!)
|
||||||
|
geometry = geom_form.save(action)
|
||||||
|
self.instance.geometry = geometry
|
||||||
|
self.instance.save()
|
||||||
|
|
||||||
|
return self.instance
|
||||||
|
@ -94,8 +94,6 @@ class NewEcoAccountForm(AbstractCompensationForm, CompensationResponsibleFormMix
|
|||||||
|
|
||||||
# Create log entry
|
# Create log entry
|
||||||
action = UserActionLogEntry.get_created_action(user)
|
action = UserActionLogEntry.get_created_action(user)
|
||||||
# Process the geometry form
|
|
||||||
geometry = geom_form.save(action)
|
|
||||||
|
|
||||||
handler = Handler.objects.create(
|
handler = Handler.objects.create(
|
||||||
type=handler_type,
|
type=handler_type,
|
||||||
@ -119,7 +117,6 @@ class NewEcoAccountForm(AbstractCompensationForm, CompensationResponsibleFormMix
|
|||||||
responsible=responsible,
|
responsible=responsible,
|
||||||
deductable_surface=surface,
|
deductable_surface=surface,
|
||||||
created=action,
|
created=action,
|
||||||
geometry=geometry,
|
|
||||||
comment=comment,
|
comment=comment,
|
||||||
is_pik=is_pik,
|
is_pik=is_pik,
|
||||||
legal=legal
|
legal=legal
|
||||||
@ -129,6 +126,10 @@ class NewEcoAccountForm(AbstractCompensationForm, CompensationResponsibleFormMix
|
|||||||
# Add the log entry to the main objects log list
|
# Add the log entry to the main objects log list
|
||||||
acc.log.add(action)
|
acc.log.add(action)
|
||||||
|
|
||||||
|
# Process the geometry form
|
||||||
|
geometry = geom_form.save(action)
|
||||||
|
acc.geometry = geometry
|
||||||
|
acc.save()
|
||||||
acc.update_deductable_rest()
|
acc.update_deductable_rest()
|
||||||
return acc
|
return acc
|
||||||
|
|
||||||
@ -185,9 +186,6 @@ class EditEcoAccountForm(NewEcoAccountForm):
|
|||||||
# Create log entry
|
# Create log entry
|
||||||
action = UserActionLogEntry.get_edited_action(user)
|
action = UserActionLogEntry.get_edited_action(user)
|
||||||
|
|
||||||
# Process the geometry form
|
|
||||||
geometry = geom_form.save(action)
|
|
||||||
|
|
||||||
# Update responsible data
|
# Update responsible data
|
||||||
self.instance.responsible.handler.type = handler_type
|
self.instance.responsible.handler.type = handler_type
|
||||||
self.instance.responsible.handler.detail = handler_detail
|
self.instance.responsible.handler.detail = handler_detail
|
||||||
@ -204,7 +202,6 @@ class EditEcoAccountForm(NewEcoAccountForm):
|
|||||||
self.instance.identifier = identifier
|
self.instance.identifier = identifier
|
||||||
self.instance.title = title
|
self.instance.title = title
|
||||||
self.instance.deductable_surface = surface
|
self.instance.deductable_surface = surface
|
||||||
self.instance.geometry = geometry
|
|
||||||
self.instance.comment = comment
|
self.instance.comment = comment
|
||||||
self.instance.is_pik = is_pik
|
self.instance.is_pik = is_pik
|
||||||
self.instance.modified = action
|
self.instance.modified = action
|
||||||
@ -213,6 +210,10 @@ class EditEcoAccountForm(NewEcoAccountForm):
|
|||||||
# Add the log entry to the main objects log list
|
# Add the log entry to the main objects log list
|
||||||
self.instance.log.add(action)
|
self.instance.log.add(action)
|
||||||
|
|
||||||
|
# Process the geometry form (NOT ATOMIC TRANSACTION DUE TO CELERY!)
|
||||||
|
geometry = geom_form.save(action)
|
||||||
|
self.instance.geometry = geometry
|
||||||
|
self.instance.save()
|
||||||
self.instance.update_deductable_rest()
|
self.instance.update_deductable_rest()
|
||||||
return self.instance
|
return self.instance
|
||||||
|
|
||||||
|
18
compensation/migrations/0014_auto_20221118_1620.py
Normal file
18
compensation/migrations/0014_auto_20221118_1620.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 3.1.3 on 2022-11-18 15:20
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('compensation', '0013_auto_20221117_0819'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='compensationaction',
|
||||||
|
name='unit',
|
||||||
|
field=models.CharField(blank=True, choices=[('cm', 'cm'), ('m', 'm'), ('m2', 'm²'), ('m3', 'm³'), ('km', 'km'), ('ha', 'ha'), ('pcs', 'Pieces')], max_length=100, null=True),
|
||||||
|
),
|
||||||
|
]
|
@ -118,8 +118,15 @@ class EcoAccount(AbstractCompensation, ShareableObjectMixin, RecordableObjectMix
|
|||||||
intervention__deleted=None,
|
intervention__deleted=None,
|
||||||
)
|
)
|
||||||
deductions_surfaces = deductions.aggregate(Sum("surface"))["surface__sum"] or 0
|
deductions_surfaces = deductions.aggregate(Sum("surface"))["surface__sum"] or 0
|
||||||
available_surfaces = self.deductable_surface or deductions_surfaces ## no division by zero
|
|
||||||
ret_val = available_surfaces - deductions_surfaces
|
available_surface = self.deductable_surface
|
||||||
|
if available_surface is None:
|
||||||
|
# Fallback!
|
||||||
|
available_surface = deductions_surfaces
|
||||||
|
else:
|
||||||
|
available_surface = float(available_surface)
|
||||||
|
|
||||||
|
ret_val = available_surface - deductions_surfaces
|
||||||
|
|
||||||
return ret_val
|
return ret_val
|
||||||
|
|
||||||
|
@ -188,7 +188,7 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase):
|
|||||||
|
|
||||||
# Prepare data for deduction creation
|
# Prepare data for deduction creation
|
||||||
deduct_url = reverse("compensation:acc:new-deduction", args=(self.eco_account.id,))
|
deduct_url = reverse("compensation:acc:new-deduction", args=(self.eco_account.id,))
|
||||||
test_surface = 10.00
|
test_surface = 10.50
|
||||||
post_data = {
|
post_data = {
|
||||||
"surface": test_surface,
|
"surface": test_surface,
|
||||||
"account": self.eco_account.id,
|
"account": self.eco_account.id,
|
||||||
@ -207,7 +207,7 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase):
|
|||||||
# Make sure the deductible surface is valid for the request
|
# Make sure the deductible surface is valid for the request
|
||||||
self.eco_account.set_recorded(self.superuser)
|
self.eco_account.set_recorded(self.superuser)
|
||||||
self.eco_account.refresh_from_db()
|
self.eco_account.refresh_from_db()
|
||||||
self.eco_account.deductable_surface = test_surface + 1.00
|
self.eco_account.deductable_surface = test_surface + 1.0
|
||||||
self.eco_account.save()
|
self.eco_account.save()
|
||||||
self.assertIsNotNone(self.eco_account.recorded)
|
self.assertIsNotNone(self.eco_account.recorded)
|
||||||
self.assertGreater(self.eco_account.deductable_surface, test_surface)
|
self.assertGreater(self.eco_account.deductable_surface, test_surface)
|
||||||
@ -244,7 +244,7 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase):
|
|||||||
deduction = EcoAccountDeduction.objects.create(
|
deduction = EcoAccountDeduction.objects.create(
|
||||||
intervention=self.intervention,
|
intervention=self.intervention,
|
||||||
account=self.eco_account,
|
account=self.eco_account,
|
||||||
surface=0
|
surface=1.10
|
||||||
)
|
)
|
||||||
self.assertEqual(1, self.intervention.deductions.count())
|
self.assertEqual(1, self.intervention.deductions.count())
|
||||||
self.assertEqual(1, self.eco_account.deductions.count())
|
self.assertEqual(1, self.eco_account.deductions.count())
|
||||||
|
16
ema/forms.py
16
ema/forms.py
@ -64,8 +64,6 @@ class NewEmaForm(AbstractCompensationForm, CompensationResponsibleFormMixin, Pik
|
|||||||
|
|
||||||
# Create log entry
|
# Create log entry
|
||||||
action = UserActionLogEntry.get_created_action(user)
|
action = UserActionLogEntry.get_created_action(user)
|
||||||
# Process the geometry form
|
|
||||||
geometry = geom_form.save(action)
|
|
||||||
|
|
||||||
handler = Handler.objects.create(
|
handler = Handler.objects.create(
|
||||||
type=handler_type,
|
type=handler_type,
|
||||||
@ -83,7 +81,6 @@ class NewEmaForm(AbstractCompensationForm, CompensationResponsibleFormMixin, Pik
|
|||||||
title=title,
|
title=title,
|
||||||
responsible=responsible,
|
responsible=responsible,
|
||||||
created=action,
|
created=action,
|
||||||
geometry=geometry,
|
|
||||||
comment=comment,
|
comment=comment,
|
||||||
is_pik=is_pik,
|
is_pik=is_pik,
|
||||||
)
|
)
|
||||||
@ -93,6 +90,11 @@ class NewEmaForm(AbstractCompensationForm, CompensationResponsibleFormMixin, Pik
|
|||||||
|
|
||||||
# Add the log entry to the main objects log list
|
# Add the log entry to the main objects log list
|
||||||
acc.log.add(action)
|
acc.log.add(action)
|
||||||
|
|
||||||
|
# Process the geometry form (NOT ATOMIC TRANSACTION DUE TO CELERY!)
|
||||||
|
geometry = geom_form.save(action)
|
||||||
|
acc.geometry = geometry
|
||||||
|
acc.save()
|
||||||
return acc
|
return acc
|
||||||
|
|
||||||
|
|
||||||
@ -141,8 +143,6 @@ class EditEmaForm(NewEmaForm):
|
|||||||
|
|
||||||
# Create log entry
|
# Create log entry
|
||||||
action = UserActionLogEntry.get_edited_action(user)
|
action = UserActionLogEntry.get_edited_action(user)
|
||||||
# Process the geometry form
|
|
||||||
geometry = geom_form.save(action)
|
|
||||||
|
|
||||||
# Update responsible data
|
# Update responsible data
|
||||||
self.instance.responsible.handler.type = handler_type
|
self.instance.responsible.handler.type = handler_type
|
||||||
@ -155,7 +155,6 @@ class EditEmaForm(NewEmaForm):
|
|||||||
# Update main oject data
|
# Update main oject data
|
||||||
self.instance.identifier = identifier
|
self.instance.identifier = identifier
|
||||||
self.instance.title = title
|
self.instance.title = title
|
||||||
self.instance.geometry = geometry
|
|
||||||
self.instance.comment = comment
|
self.instance.comment = comment
|
||||||
self.instance.is_pik = is_pik
|
self.instance.is_pik = is_pik
|
||||||
self.instance.modified = action
|
self.instance.modified = action
|
||||||
@ -163,6 +162,11 @@ class EditEmaForm(NewEmaForm):
|
|||||||
|
|
||||||
# Add the log entry to the main objects log list
|
# Add the log entry to the main objects log list
|
||||||
self.instance.log.add(action)
|
self.instance.log.add(action)
|
||||||
|
|
||||||
|
# Process the geometry form (NOT ATOMIC TRANSACTION DUE TO CELERY!)
|
||||||
|
geometry = geom_form.save(action)
|
||||||
|
self.instance.geometry = geometry
|
||||||
|
self.instance.save()
|
||||||
return self.instance
|
return self.instance
|
||||||
|
|
||||||
|
|
||||||
|
@ -263,9 +263,6 @@ class NewInterventionForm(BaseForm):
|
|||||||
handler=handler,
|
handler=handler,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Process the geometry form
|
|
||||||
geometry = geom_form.save(action)
|
|
||||||
|
|
||||||
# Finally create main object, holding the other objects
|
# Finally create main object, holding the other objects
|
||||||
intervention = Intervention.objects.create(
|
intervention = Intervention.objects.create(
|
||||||
identifier=identifier,
|
identifier=identifier,
|
||||||
@ -273,7 +270,6 @@ class NewInterventionForm(BaseForm):
|
|||||||
responsible=responsibility_data,
|
responsible=responsibility_data,
|
||||||
legal=legal_data,
|
legal=legal_data,
|
||||||
created=action,
|
created=action,
|
||||||
geometry=geometry,
|
|
||||||
comment=comment,
|
comment=comment,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -282,6 +278,12 @@ class NewInterventionForm(BaseForm):
|
|||||||
|
|
||||||
# Add the performing user as the first user having access to the data
|
# Add the performing user as the first user having access to the data
|
||||||
intervention.share_with_user(user)
|
intervention.share_with_user(user)
|
||||||
|
|
||||||
|
# Process the geometry form (NOT ATOMIC TRANSACTION DUE TO CELERY!)
|
||||||
|
geometry = geom_form.save(action)
|
||||||
|
intervention.geometry = geometry
|
||||||
|
intervention.save()
|
||||||
|
|
||||||
return intervention
|
return intervention
|
||||||
|
|
||||||
|
|
||||||
@ -370,9 +372,6 @@ class EditInterventionForm(NewInterventionForm):
|
|||||||
|
|
||||||
user_action = self.instance.mark_as_edited(user, edit_comment=EDITED_GENERAL_DATA)
|
user_action = self.instance.mark_as_edited(user, edit_comment=EDITED_GENERAL_DATA)
|
||||||
|
|
||||||
geometry = geom_form.save(user_action)
|
|
||||||
self.instance.geometry = geometry
|
|
||||||
|
|
||||||
self.instance.log.add(user_action)
|
self.instance.log.add(user_action)
|
||||||
|
|
||||||
self.instance.identifier = identifier
|
self.instance.identifier = identifier
|
||||||
@ -381,5 +380,10 @@ class EditInterventionForm(NewInterventionForm):
|
|||||||
self.instance.modified = user_action
|
self.instance.modified = user_action
|
||||||
self.instance.save()
|
self.instance.save()
|
||||||
|
|
||||||
|
# Process the geometry form (NOT ATOMIC TRANSACTION DUE TO CELERY!)
|
||||||
|
geometry = geom_form.save(user_action)
|
||||||
|
self.instance.geometry = geometry
|
||||||
|
self.instance.save()
|
||||||
|
|
||||||
return self.instance
|
return self.instance
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
|
|
||||||
from konova.forms.base_form import BaseForm
|
from konova.forms.base_form import BaseForm
|
||||||
from konova.models import Geometry
|
from konova.models import Geometry
|
||||||
from konova.tasks import celery_update_parcels
|
from konova.tasks import celery_update_parcels, celery_check_for_geometry_conflicts
|
||||||
from konova.sub_settings.lanis_settings import DEFAULT_SRID_RLP
|
from konova.sub_settings.lanis_settings import DEFAULT_SRID_RLP
|
||||||
from user.models import UserActionLogEntry
|
from user.models import UserActionLogEntry
|
||||||
|
|
||||||
@ -63,6 +63,7 @@ class SimpleGeomForm(BaseForm):
|
|||||||
geom = self.data["geom"]
|
geom = self.data["geom"]
|
||||||
if geom is None or len(geom) == 0:
|
if geom is None or len(geom) == 0:
|
||||||
# empty geometry is a valid geometry
|
# empty geometry is a valid geometry
|
||||||
|
self.cleaned_data["geom"] = MultiPolygon(srid=DEFAULT_SRID_RLP).ewkt
|
||||||
return is_valid
|
return is_valid
|
||||||
geom = json.loads(geom)
|
geom = json.loads(geom)
|
||||||
|
|
||||||
@ -82,7 +83,12 @@ class SimpleGeomForm(BaseForm):
|
|||||||
"MultiPolygon25D",
|
"MultiPolygon25D",
|
||||||
]
|
]
|
||||||
for feature in features_json:
|
for feature in features_json:
|
||||||
feature_geom = json.dumps(feature.get("geometry", feature))
|
feature_geom = feature.get("geometry", feature)
|
||||||
|
if feature_geom is None:
|
||||||
|
# Fallback for rare cases where a feature does not contain any geometry
|
||||||
|
continue
|
||||||
|
|
||||||
|
feature_geom = json.dumps(feature_geom)
|
||||||
g = gdal.OGRGeometry(feature_geom, srs=DEFAULT_SRID_RLP)
|
g = gdal.OGRGeometry(feature_geom, srs=DEFAULT_SRID_RLP)
|
||||||
|
|
||||||
flatten_geometry = g.coord_dim > 2
|
flatten_geometry = g.coord_dim > 2
|
||||||
@ -101,6 +107,8 @@ class SimpleGeomForm(BaseForm):
|
|||||||
return is_valid
|
return is_valid
|
||||||
|
|
||||||
features.append(polygon)
|
features.append(polygon)
|
||||||
|
|
||||||
|
# Unionize all geometry features into one new MultiPolygon
|
||||||
form_geom = MultiPolygon(srid=DEFAULT_SRID_RLP)
|
form_geom = MultiPolygon(srid=DEFAULT_SRID_RLP)
|
||||||
for feature in features:
|
for feature in features:
|
||||||
form_geom = form_geom.union(feature)
|
form_geom = form_geom.union(feature)
|
||||||
@ -141,8 +149,9 @@ class SimpleGeomForm(BaseForm):
|
|||||||
geom=self.cleaned_data.get("geom", MultiPolygon(srid=DEFAULT_SRID_RLP)),
|
geom=self.cleaned_data.get("geom", MultiPolygon(srid=DEFAULT_SRID_RLP)),
|
||||||
created=action,
|
created=action,
|
||||||
)
|
)
|
||||||
# Start the parcel update procedure in a background process
|
# Start parcel update and geometry conflict checking procedure in a background process
|
||||||
celery_update_parcels.delay(geometry.id)
|
celery_update_parcels.delay(geometry.id)
|
||||||
|
celery_check_for_geometry_conflicts.delay(geometry.id)
|
||||||
return geometry
|
return geometry
|
||||||
|
|
||||||
def __flatten_geom_to_2D(self, geom):
|
def __flatten_geom_to_2D(self, geom):
|
||||||
|
@ -9,16 +9,15 @@ import json
|
|||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
from django.contrib.gis.db.models import MultiPolygonField
|
from django.contrib.gis.db.models import MultiPolygonField
|
||||||
from django.contrib.gis.geos import Polygon
|
|
||||||
from django.core.exceptions import MultipleObjectsReturned
|
from django.core.exceptions import MultipleObjectsReturned
|
||||||
from django.db import models
|
from django.db import models, transaction
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from konova.models import BaseResource, UuidModel
|
from konova.models import BaseResource, UuidModel
|
||||||
from konova.sub_settings.lanis_settings import DEFAULT_SRID_RLP
|
from konova.sub_settings.lanis_settings import DEFAULT_SRID_RLP
|
||||||
|
from konova.utils.wfs.spatial import ParcelWFSFetcher
|
||||||
from konova.utils.mutex import cache_lock
|
from konova.utils.mutex import cache_lock
|
||||||
from konova.tasks import celery_check_for_geometry_conflicts
|
from konova.tasks import celery_check_for_geometry_conflicts
|
||||||
from konova.utils.wfs.spatial import ParcelWFSFetcher
|
|
||||||
|
|
||||||
|
|
||||||
class Geometry(BaseResource):
|
class Geometry(BaseResource):
|
||||||
@ -32,7 +31,18 @@ class Geometry(BaseResource):
|
|||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
celery_check_for_geometry_conflicts.delay(self.id)
|
|
||||||
|
@property
|
||||||
|
def geom_small_buffered(self):
|
||||||
|
"""
|
||||||
|
Returns a smaller buffered version of the geometry.
|
||||||
|
Can be used to shrink the geometry used for intersection purposes to avoid intersection detection on
|
||||||
|
neighbouring geometries.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
return self.geom.buffer(-0.001)
|
||||||
|
|
||||||
def check_for_conflicts(self):
|
def check_for_conflicts(self):
|
||||||
""" Checks for new geometry overlaps
|
""" Checks for new geometry overlaps
|
||||||
@ -47,9 +57,8 @@ class Geometry(BaseResource):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
self.recheck_existing_conflicts()
|
self.recheck_existing_conflicts()
|
||||||
|
|
||||||
overlapping_geoms = Geometry.objects.filter(
|
overlapping_geoms = Geometry.objects.filter(
|
||||||
geom__intersects=self.geom,
|
geom__intersects=self.geom_small_buffered,
|
||||||
).exclude(
|
).exclude(
|
||||||
id=self.id
|
id=self.id
|
||||||
).distinct()
|
).distinct()
|
||||||
@ -71,14 +80,14 @@ class Geometry(BaseResource):
|
|||||||
"""
|
"""
|
||||||
all_conflicts_as_conflicting = self.conflicts_geometries.all()
|
all_conflicts_as_conflicting = self.conflicts_geometries.all()
|
||||||
still_conflicting_conflicts = all_conflicts_as_conflicting.filter(
|
still_conflicting_conflicts = all_conflicts_as_conflicting.filter(
|
||||||
affected_geometry__geom__intersects=self.geom
|
affected_geometry__geom__intersects=self.geom_small_buffered
|
||||||
)
|
)
|
||||||
resolved_conflicts = all_conflicts_as_conflicting.exclude(id__in=still_conflicting_conflicts)
|
resolved_conflicts = all_conflicts_as_conflicting.exclude(id__in=still_conflicting_conflicts)
|
||||||
resolved_conflicts.delete()
|
resolved_conflicts.delete()
|
||||||
|
|
||||||
all_conflicted_by_conflicts = self.conflicted_by_geometries.all()
|
all_conflicted_by_conflicts = self.conflicted_by_geometries.all()
|
||||||
still_conflicting_conflicts = all_conflicted_by_conflicts.filter(
|
still_conflicting_conflicts = all_conflicted_by_conflicts.filter(
|
||||||
conflicting_geometry__geom__intersects=self.geom
|
conflicting_geometry__geom__intersects=self.geom_small_buffered
|
||||||
)
|
)
|
||||||
resolved_conflicts = all_conflicted_by_conflicts.exclude(id__in=still_conflicting_conflicts)
|
resolved_conflicts = all_conflicted_by_conflicts.exclude(id__in=still_conflicting_conflicts)
|
||||||
resolved_conflicts.delete()
|
resolved_conflicts.delete()
|
||||||
@ -110,6 +119,11 @@ class Geometry(BaseResource):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
from konova.models import Parcel, District, ParcelIntersection, Municipal, ParcelGroup
|
from konova.models import Parcel, District, ParcelIntersection, Municipal, ParcelGroup
|
||||||
|
|
||||||
|
if self.geom.empty:
|
||||||
|
# Nothing to do
|
||||||
|
return
|
||||||
|
|
||||||
parcel_fetcher = ParcelWFSFetcher(
|
parcel_fetcher = ParcelWFSFetcher(
|
||||||
geometry_id=self.id,
|
geometry_id=self.id,
|
||||||
)
|
)
|
||||||
|
@ -15,7 +15,7 @@ DEFAULT_SRID_RLP = 25832
|
|||||||
|
|
||||||
# Needed to redirect to LANIS
|
# Needed to redirect to LANIS
|
||||||
## Values to be inserted are [zoom_level, x_coord, y_coord]
|
## Values to be inserted are [zoom_level, x_coord, y_coord]
|
||||||
LANIS_LINK_TEMPLATE = "https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/index.php?lang=de&zl={}&x={}&y={}&bl=tk_rlp_tms_grau&bo=1&lo=0.8,0.8,0.8,0.6,0.8,0.8,0.8,0.8,0.8&layers=eiv_f,eiv_l,eiv_p,kom_f,kom_l,kom_p,oek_f,ema_f,mae&service=kartendienste_naturschutz"
|
LANIS_LINK_TEMPLATE = "https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/index.php?lang=de&zl={}&x={}&y={}&bl=tk_rlp_tms_grau&bo=1&lo=0.8,0.8,0.8,0.6,0.8,0.8,0.8,0.8,0.8&layers=eiv_recorded,eiv_unrecorded,kom_recorded,kom_unrecorded,oek_recorded,oek_unrecorded,ema_recorded,ema_unrecorded,mae&service=kartendienste_naturschutz"
|
||||||
## This look up table (LUT) defines different zoom levels on the size of the calculate area of a geometry.
|
## This look up table (LUT) defines different zoom levels on the size of the calculate area of a geometry.
|
||||||
LANIS_ZOOM_LUT = {
|
LANIS_ZOOM_LUT = {
|
||||||
1000000000: 6,
|
1000000000: 6,
|
||||||
|
@ -17,11 +17,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
{% if geom_form.instance.geometry %}
|
||||||
<div hx-trigger="load, every 5s" hx-get="{% url 'geometry-parcels' geom_form.instance.geometry.id %}">
|
<div hx-trigger="load, every 5s" hx-get="{% url 'geometry-parcels' geom_form.instance.geometry.id %}">
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<span class="spinner-border rlp-r-inv" role="status"></span>
|
<span class="spinner-border rlp-r-inv" role="status"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
{% translate 'No geometry entry found on database. Please contact an admin!' %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
@ -91,7 +91,7 @@ class ParcelWFSFetcher(AbstractWFSFetcher):
|
|||||||
geom = Geometry.objects.filter(
|
geom = Geometry.objects.filter(
|
||||||
id=self.geometry_id
|
id=self.geometry_id
|
||||||
).annotate(
|
).annotate(
|
||||||
smaller=Func(F('geom'), -0.001, function="ST_Buffer")
|
smaller=Func(F('geom'), -0.001, function="ST_Buffer") # same as geometry.geom_small_buffered but for QuerySet
|
||||||
).annotate(
|
).annotate(
|
||||||
gml=AsGML(MakeValid('smaller'))
|
gml=AsGML(MakeValid('smaller'))
|
||||||
).first()
|
).first()
|
||||||
|
@ -32,16 +32,16 @@ def get_geom_parcels(request: HttpRequest, id: str):
|
|||||||
parcels = geom.get_underlying_parcels()
|
parcels = geom.get_underlying_parcels()
|
||||||
geos_geom = geom.geom
|
geos_geom = geom.geom
|
||||||
|
|
||||||
parcels_are_currently_calculated = geos_geom is not None and geos_geom.area > 0 and len(parcels) == 0
|
geometry_exists = not geos_geom.empty
|
||||||
|
parcels_are_currently_calculated = geometry_exists and geos_geom.area > 0 and len(parcels) == 0
|
||||||
parcels_available = len(parcels) > 0
|
parcels_available = len(parcels) > 0
|
||||||
no_geometry_given = geos_geom is None
|
|
||||||
|
|
||||||
if parcels_are_currently_calculated:
|
if parcels_are_currently_calculated:
|
||||||
# Parcels are being calculated right now. Change the status code, so polling stays active for fetching
|
# Parcels are being calculated right now. Change the status code, so polling stays active for fetching
|
||||||
# resutls after the calculation
|
# resutls after the calculation
|
||||||
status_code = 200
|
status_code = 200
|
||||||
|
|
||||||
if parcels_available or no_geometry_given:
|
if parcels_available or not geometry_exists:
|
||||||
parcels = parcels.order_by("-municipal", "flr", "flrstck_zhlr", "flrstck_nnr")
|
parcels = parcels.order_by("-municipal", "flr", "flrstck_zhlr", "flrstck_nnr")
|
||||||
municipals = parcels.order_by("municipal").distinct("municipal").values("municipal__id")
|
municipals = parcels.order_by("municipal").distinct("municipal").values("municipal__id")
|
||||||
municipals = Municipal.objects.filter(id__in=municipals)
|
municipals = Municipal.objects.filter(id__in=municipals)
|
||||||
|
Binary file not shown.
@ -2307,8 +2307,8 @@ msgid "Server Error"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/500.html:10
|
#: templates/500.html:10
|
||||||
msgid "Something happened. We are working on it!"
|
msgid "Something happened. Admins have been informed. We are working on it!"
|
||||||
msgstr "Irgendetwas ist passiert. Wir arbeiten daran!"
|
msgstr "Irgendetwas ist passiert. Die Administratoren wurden informiert. Wir arbeiten daran!"
|
||||||
|
|
||||||
#: templates/email/api/verify_token.html:7
|
#: templates/email/api/verify_token.html:7
|
||||||
msgid "Hello support"
|
msgid "Hello support"
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<h1 class="display-4">{% trans 'Server Error' %}</h1>
|
<h1 class="display-4">{% trans 'Server Error' %}</h1>
|
||||||
<hr>
|
<hr>
|
||||||
<p class="lead">
|
<p class="lead">
|
||||||
{% trans 'Something happened. We are working on it!' %}
|
{% trans 'Something happened. Admins have been informed. We are working on it!' %}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
Loading…
Reference in New Issue
Block a user