Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 92211445e7 | |||
| 73c61e96f5 | |||
| 339f074681 | |||
| 989d256521 | |||
| 623c29f827 | |||
| eb975cd3c5 | |||
| 5a8765f638 | |||
| 9c2bdcdacf | |||
| 48e3e84b4c | |||
| 940aa38154 | |||
| 887a3552b4 | |||
| 3b36193566 | |||
| 090f6faa4e | |||
| d2ec3d9c08 | |||
| 8165540c00 | |||
| 87fae51144 | |||
| bf1c0e2078 | |||
| b85e33dc22 | |||
| 83d70b6d59 | |||
| 60e23d15fc | |||
| fb1dce9d3c | |||
| 6ecbd74b93 | |||
| a6551534dc | |||
| 6060f1c1bd | |||
| 59ff1c79a8 | |||
| 64d0a3bd12 | |||
| a34a0b4d8a | |||
| 1be77e8b22 | |||
| fc31ad4ae0 | |||
| 7689e0b80d | |||
| 17c954e844 | |||
| 06ad0fdc2d | |||
| 98a1a70a69 | |||
| 22a3339157 | |||
| c98f41c9a8 |
@@ -8,7 +8,10 @@
|
|||||||
"responsible": {
|
"responsible": {
|
||||||
"conservation_office": null,
|
"conservation_office": null,
|
||||||
"conservation_file_number": null,
|
"conservation_file_number": null,
|
||||||
"handler": null
|
"handler": {
|
||||||
|
"type": null,
|
||||||
|
"detail": "Someone"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"legal": {
|
"legal": {
|
||||||
"agreement_date": null
|
"agreement_date": null
|
||||||
|
|||||||
@@ -7,7 +7,10 @@
|
|||||||
"responsible": {
|
"responsible": {
|
||||||
"conservation_office": null,
|
"conservation_office": null,
|
||||||
"conservation_file_number": null,
|
"conservation_file_number": null,
|
||||||
"handler": null
|
"handler": {
|
||||||
|
"type": null,
|
||||||
|
"detail": "Someone"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"before_states": [
|
"before_states": [
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -9,7 +9,10 @@
|
|||||||
"registration_file_number": null,
|
"registration_file_number": null,
|
||||||
"conservation_office": null,
|
"conservation_office": null,
|
||||||
"conservation_file_number": null,
|
"conservation_file_number": null,
|
||||||
"handler": null
|
"handler": {
|
||||||
|
"type": null,
|
||||||
|
"detail": "Someone"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"legal": {
|
"legal": {
|
||||||
"registration_date": null,
|
"registration_date": null,
|
||||||
|
|||||||
@@ -48,7 +48,10 @@
|
|||||||
"responsible": {
|
"responsible": {
|
||||||
"conservation_office": null,
|
"conservation_office": null,
|
||||||
"conservation_file_number": "123-TEST",
|
"conservation_file_number": "123-TEST",
|
||||||
"handler": "TEST_HANDLER_CHANGED"
|
"handler": {
|
||||||
|
"type": null,
|
||||||
|
"detail": "TEST HANDLER CHANGED"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"legal": {
|
"legal": {
|
||||||
"agreement_date": "2022-01-11"
|
"agreement_date": "2022-01-11"
|
||||||
|
|||||||
@@ -47,7 +47,10 @@
|
|||||||
"responsible": {
|
"responsible": {
|
||||||
"conservation_office": null,
|
"conservation_office": null,
|
||||||
"conservation_file_number": "TEST_CHANGED",
|
"conservation_file_number": "TEST_CHANGED",
|
||||||
"handler": "TEST_HANDLER_CHANGED"
|
"handler": {
|
||||||
|
"type": null,
|
||||||
|
"detail": "TEST_HANDLER_CHANGED"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"before_states": [],
|
"before_states": [],
|
||||||
"after_states": [],
|
"after_states": [],
|
||||||
|
|||||||
@@ -49,7 +49,10 @@
|
|||||||
"registration_file_number": "CHANGED",
|
"registration_file_number": "CHANGED",
|
||||||
"conservation_office": null,
|
"conservation_office": null,
|
||||||
"conservation_file_number": "CHANGED",
|
"conservation_file_number": "CHANGED",
|
||||||
"handler": null
|
"handler": {
|
||||||
|
"type": null,
|
||||||
|
"detail": "TEST_HANDLER_CHANGED"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"legal": {
|
"legal": {
|
||||||
"registration_date": "2022-02-01",
|
"registration_date": "2022-02-01",
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ class APIV1UpdateTestCase(BaseAPIV1TestCase):
|
|||||||
self.assertEqual(put_props["deductable_surface"], str(self.eco_account.deductable_surface))
|
self.assertEqual(put_props["deductable_surface"], str(self.eco_account.deductable_surface))
|
||||||
self.assertEqual(put_props["responsible"]["conservation_office"], self.eco_account.responsible.conservation_office)
|
self.assertEqual(put_props["responsible"]["conservation_office"], self.eco_account.responsible.conservation_office)
|
||||||
self.assertEqual(put_props["responsible"]["conservation_file_number"], self.eco_account.responsible.conservation_file_number)
|
self.assertEqual(put_props["responsible"]["conservation_file_number"], self.eco_account.responsible.conservation_file_number)
|
||||||
self.assertEqual(put_props["responsible"]["handler"], self.eco_account.responsible.handler)
|
self.assertEqual(put_props["responsible"]["handler"]["detail"], self.eco_account.responsible.handler.detail)
|
||||||
self.assertEqual(put_props["legal"]["agreement_date"], str(self.eco_account.legal.registration_date))
|
self.assertEqual(put_props["legal"]["agreement_date"], str(self.eco_account.legal.registration_date))
|
||||||
self.assertEqual(len(put_props["actions"]), self.eco_account.actions.count())
|
self.assertEqual(len(put_props["actions"]), self.eco_account.actions.count())
|
||||||
self.assertEqual(len(put_props["before_states"]), self.eco_account.before_states.count())
|
self.assertEqual(len(put_props["before_states"]), self.eco_account.before_states.count())
|
||||||
@@ -156,7 +156,7 @@ class APIV1UpdateTestCase(BaseAPIV1TestCase):
|
|||||||
self.assertNotEqual(modified_on, self.ema.modified)
|
self.assertNotEqual(modified_on, self.ema.modified)
|
||||||
self.assertEqual(put_props["responsible"]["conservation_office"], self.ema.responsible.conservation_office)
|
self.assertEqual(put_props["responsible"]["conservation_office"], self.ema.responsible.conservation_office)
|
||||||
self.assertEqual(put_props["responsible"]["conservation_file_number"], self.ema.responsible.conservation_file_number)
|
self.assertEqual(put_props["responsible"]["conservation_file_number"], self.ema.responsible.conservation_file_number)
|
||||||
self.assertEqual(put_props["responsible"]["handler"], self.ema.responsible.handler)
|
self.assertEqual(put_props["responsible"]["handler"]["detail"], self.ema.responsible.handler.detail)
|
||||||
self.assertEqual(len(put_props["actions"]), self.ema.actions.count())
|
self.assertEqual(len(put_props["actions"]), self.ema.actions.count())
|
||||||
self.assertEqual(len(put_props["before_states"]), self.ema.before_states.count())
|
self.assertEqual(len(put_props["before_states"]), self.ema.before_states.count())
|
||||||
self.assertEqual(len(put_props["after_states"]), self.ema.after_states.count())
|
self.assertEqual(len(put_props["after_states"]), self.ema.after_states.count())
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ from django.db import transaction
|
|||||||
|
|
||||||
from api.utils.serializer.v1.serializer import AbstractModelAPISerializerV1, AbstractCompensationAPISerializerV1Mixin, \
|
from api.utils.serializer.v1.serializer import AbstractModelAPISerializerV1, AbstractCompensationAPISerializerV1Mixin, \
|
||||||
LegalAPISerializerV1Mixin, ResponsibilityAPISerializerV1Mixin, DeductableAPISerializerV1Mixin
|
LegalAPISerializerV1Mixin, ResponsibilityAPISerializerV1Mixin, DeductableAPISerializerV1Mixin
|
||||||
from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID
|
from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID, CODELIST_HANDLER_ID
|
||||||
from compensation.models import EcoAccount
|
from compensation.models import EcoAccount
|
||||||
from intervention.models import Legal, Responsibility
|
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
|
||||||
from user.models import UserActionLogEntry
|
from user.models import UserActionLogEntry
|
||||||
@@ -44,7 +44,7 @@ class EcoAccountAPISerializerV1(AbstractModelAPISerializerV1,
|
|||||||
return {
|
return {
|
||||||
"conservation_office": self._konova_code_to_json(responsible.conservation_office),
|
"conservation_office": self._konova_code_to_json(responsible.conservation_office),
|
||||||
"conservation_file_number": responsible.conservation_file_number,
|
"conservation_file_number": responsible.conservation_file_number,
|
||||||
"handler": responsible.handler,
|
"handler": self._handler_to_json(responsible.handler),
|
||||||
}
|
}
|
||||||
|
|
||||||
def _set_responsibility(self, obj, responsibility_data: dict):
|
def _set_responsibility(self, obj, responsibility_data: dict):
|
||||||
@@ -64,7 +64,11 @@ class EcoAccountAPISerializerV1(AbstractModelAPISerializerV1,
|
|||||||
CODELIST_CONSERVATION_OFFICE_ID,
|
CODELIST_CONSERVATION_OFFICE_ID,
|
||||||
)
|
)
|
||||||
obj.responsible.conservation_file_number = responsibility_data["conservation_file_number"]
|
obj.responsible.conservation_file_number = responsibility_data["conservation_file_number"]
|
||||||
obj.responsible.handler = responsibility_data["handler"]
|
obj.responsible.handler.type = self._konova_code_from_json(
|
||||||
|
responsibility_data["handler"]["type"],
|
||||||
|
CODELIST_HANDLER_ID,
|
||||||
|
)
|
||||||
|
obj.responsible.handler.detail = responsibility_data["handler"]["detail"]
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
def _set_legal(self, obj, legal_data):
|
def _set_legal(self, obj, legal_data):
|
||||||
@@ -92,7 +96,9 @@ class EcoAccountAPISerializerV1(AbstractModelAPISerializerV1,
|
|||||||
|
|
||||||
# Create linked objects
|
# Create linked objects
|
||||||
obj = EcoAccount()
|
obj = EcoAccount()
|
||||||
obj.responsible = Responsibility()
|
obj.responsible = Responsibility(
|
||||||
|
handler=Handler()
|
||||||
|
)
|
||||||
obj.legal = Legal()
|
obj.legal = Legal()
|
||||||
created = create_action
|
created = create_action
|
||||||
obj.created = created
|
obj.created = created
|
||||||
@@ -128,6 +134,7 @@ class EcoAccountAPISerializerV1(AbstractModelAPISerializerV1,
|
|||||||
obj = self._set_legal(obj, properties["legal"])
|
obj = self._set_legal(obj, properties["legal"])
|
||||||
|
|
||||||
obj.geometry.save()
|
obj.geometry.save()
|
||||||
|
obj.responsible.handler.save()
|
||||||
obj.responsible.save()
|
obj.responsible.save()
|
||||||
obj.legal.save()
|
obj.legal.save()
|
||||||
obj.save()
|
obj.save()
|
||||||
@@ -170,6 +177,7 @@ class EcoAccountAPISerializerV1(AbstractModelAPISerializerV1,
|
|||||||
obj = self._set_legal(obj, properties["legal"])
|
obj = self._set_legal(obj, properties["legal"])
|
||||||
|
|
||||||
obj.geometry.save()
|
obj.geometry.save()
|
||||||
|
obj.responsible.handler.save()
|
||||||
obj.responsible.save()
|
obj.responsible.save()
|
||||||
obj.legal.save()
|
obj.legal.save()
|
||||||
obj.save()
|
obj.save()
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ from django.db import transaction
|
|||||||
|
|
||||||
from api.utils.serializer.v1.serializer import AbstractModelAPISerializerV1, AbstractCompensationAPISerializerV1Mixin, \
|
from api.utils.serializer.v1.serializer import AbstractModelAPISerializerV1, AbstractCompensationAPISerializerV1Mixin, \
|
||||||
ResponsibilityAPISerializerV1Mixin
|
ResponsibilityAPISerializerV1Mixin
|
||||||
from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID
|
from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID, CODELIST_HANDLER_ID
|
||||||
from ema.models import Ema
|
from ema.models import Ema
|
||||||
from intervention.models import Responsibility
|
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
|
||||||
from user.models import UserActionLogEntry
|
from user.models import UserActionLogEntry
|
||||||
@@ -31,7 +31,7 @@ class EmaAPISerializerV1(AbstractModelAPISerializerV1, AbstractCompensationAPISe
|
|||||||
return {
|
return {
|
||||||
"conservation_office": self._konova_code_to_json(responsible.conservation_office),
|
"conservation_office": self._konova_code_to_json(responsible.conservation_office),
|
||||||
"conservation_file_number": responsible.conservation_file_number,
|
"conservation_file_number": responsible.conservation_file_number,
|
||||||
"handler": responsible.handler,
|
"handler": self._handler_to_json(responsible.handler),
|
||||||
}
|
}
|
||||||
|
|
||||||
def _set_responsibility(self, obj, responsibility_data: dict):
|
def _set_responsibility(self, obj, responsibility_data: dict):
|
||||||
@@ -51,7 +51,11 @@ class EmaAPISerializerV1(AbstractModelAPISerializerV1, AbstractCompensationAPISe
|
|||||||
CODELIST_CONSERVATION_OFFICE_ID,
|
CODELIST_CONSERVATION_OFFICE_ID,
|
||||||
)
|
)
|
||||||
obj.responsible.conservation_file_number = responsibility_data["conservation_file_number"]
|
obj.responsible.conservation_file_number = responsibility_data["conservation_file_number"]
|
||||||
obj.responsible.handler = responsibility_data["handler"]
|
obj.responsible.handler.type = self._konova_code_from_json(
|
||||||
|
responsibility_data["handler"]["type"],
|
||||||
|
CODELIST_HANDLER_ID,
|
||||||
|
)
|
||||||
|
obj.responsible.handler.detail = responsibility_data["handler"]["detail"]
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
def _initialize_objects(self, json_model, user):
|
def _initialize_objects(self, json_model, user):
|
||||||
@@ -75,7 +79,9 @@ class EmaAPISerializerV1(AbstractModelAPISerializerV1, AbstractCompensationAPISe
|
|||||||
|
|
||||||
# Create linked objects
|
# Create linked objects
|
||||||
obj = Ema()
|
obj = Ema()
|
||||||
obj.responsible = Responsibility()
|
obj.responsible = Responsibility(
|
||||||
|
handler=Handler()
|
||||||
|
)
|
||||||
created = create_action
|
created = create_action
|
||||||
obj.created = created
|
obj.created = created
|
||||||
obj.geometry = geometry
|
obj.geometry = geometry
|
||||||
@@ -101,6 +107,7 @@ class EmaAPISerializerV1(AbstractModelAPISerializerV1, AbstractCompensationAPISe
|
|||||||
obj = self._set_responsibility(obj, properties["responsible"])
|
obj = self._set_responsibility(obj, properties["responsible"])
|
||||||
|
|
||||||
obj.geometry.save()
|
obj.geometry.save()
|
||||||
|
obj.responsible.handler.save()
|
||||||
obj.responsible.save()
|
obj.responsible.save()
|
||||||
obj.save()
|
obj.save()
|
||||||
|
|
||||||
@@ -140,6 +147,7 @@ class EmaAPISerializerV1(AbstractModelAPISerializerV1, AbstractCompensationAPISe
|
|||||||
obj = self._set_responsibility(obj, properties["responsible"])
|
obj = self._set_responsibility(obj, properties["responsible"])
|
||||||
|
|
||||||
obj.geometry.save()
|
obj.geometry.save()
|
||||||
|
obj.responsible.handler.save()
|
||||||
obj.responsible.save()
|
obj.responsible.save()
|
||||||
obj.save()
|
obj.save()
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from django.db.models import QuerySet
|
|||||||
from api.utils.serializer.v1.serializer import AbstractModelAPISerializerV1, \
|
from api.utils.serializer.v1.serializer import AbstractModelAPISerializerV1, \
|
||||||
ResponsibilityAPISerializerV1Mixin, LegalAPISerializerV1Mixin, DeductableAPISerializerV1Mixin
|
ResponsibilityAPISerializerV1Mixin, LegalAPISerializerV1Mixin, DeductableAPISerializerV1Mixin
|
||||||
from compensation.models import Payment
|
from compensation.models import Payment
|
||||||
from intervention.models import Intervention, Responsibility, Legal
|
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
|
||||||
from user.models import UserActionLogEntry
|
from user.models import UserActionLogEntry
|
||||||
@@ -69,7 +69,9 @@ class InterventionAPISerializerV1(AbstractModelAPISerializerV1,
|
|||||||
|
|
||||||
# Create linked objects
|
# Create linked objects
|
||||||
obj = Intervention()
|
obj = Intervention()
|
||||||
resp = Responsibility()
|
resp = Responsibility(
|
||||||
|
handler=Handler()
|
||||||
|
)
|
||||||
legal = Legal()
|
legal = Legal()
|
||||||
created = create_action
|
created = create_action
|
||||||
obj.legal = legal
|
obj.legal = legal
|
||||||
@@ -130,6 +132,7 @@ class InterventionAPISerializerV1(AbstractModelAPISerializerV1,
|
|||||||
id__in=payments
|
id__in=payments
|
||||||
)
|
)
|
||||||
obj.payments.set(payments)
|
obj.payments.set(payments)
|
||||||
|
obj.send_data_to_egon()
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
def create_model_from_json(self, json_model, user):
|
def create_model_from_json(self, json_model, user):
|
||||||
@@ -152,6 +155,7 @@ class InterventionAPISerializerV1(AbstractModelAPISerializerV1,
|
|||||||
self._set_responsibility(obj, properties["responsible"])
|
self._set_responsibility(obj, properties["responsible"])
|
||||||
self._set_legal(obj, properties["legal"])
|
self._set_legal(obj, properties["legal"])
|
||||||
|
|
||||||
|
obj.responsible.handler.save()
|
||||||
obj.responsible.save()
|
obj.responsible.save()
|
||||||
obj.geometry.save()
|
obj.geometry.save()
|
||||||
obj.legal.save()
|
obj.legal.save()
|
||||||
@@ -188,12 +192,13 @@ class InterventionAPISerializerV1(AbstractModelAPISerializerV1,
|
|||||||
obj.geometry.geom = self._create_geometry_from_json(json_model)
|
obj.geometry.geom = self._create_geometry_from_json(json_model)
|
||||||
obj.geometry.modified = update_action
|
obj.geometry.modified = update_action
|
||||||
|
|
||||||
|
obj.responsible.handler.save()
|
||||||
obj.responsible.save()
|
obj.responsible.save()
|
||||||
obj.geometry.save()
|
obj.geometry.save()
|
||||||
obj.legal.save()
|
obj.legal.save()
|
||||||
obj.save()
|
obj.save()
|
||||||
|
|
||||||
obj.mark_as_edited(user)
|
obj.mark_as_edited(user, edit_comment="API update")
|
||||||
|
|
||||||
celery_update_parcels.delay(obj.geometry.id)
|
celery_update_parcels.delay(obj.geometry.id)
|
||||||
|
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ from api.utils.serializer.serializer import AbstractModelAPISerializer
|
|||||||
from codelist.models import KonovaCode
|
from codelist.models import KonovaCode
|
||||||
from codelist.settings import CODELIST_COMPENSATION_ACTION_ID, CODELIST_BIOTOPES_ID, CODELIST_PROCESS_TYPE_ID, \
|
from codelist.settings import CODELIST_COMPENSATION_ACTION_ID, CODELIST_BIOTOPES_ID, CODELIST_PROCESS_TYPE_ID, \
|
||||||
CODELIST_LAW_ID, CODELIST_REGISTRATION_OFFICE_ID, CODELIST_CONSERVATION_OFFICE_ID, \
|
CODELIST_LAW_ID, CODELIST_REGISTRATION_OFFICE_ID, CODELIST_CONSERVATION_OFFICE_ID, \
|
||||||
CODELIST_COMPENSATION_ACTION_DETAIL_ID, CODELIST_BIOTOPES_EXTRA_CODES_ID
|
CODELIST_COMPENSATION_ACTION_DETAIL_ID, CODELIST_BIOTOPES_EXTRA_CODES_ID, CODELIST_HANDLER_ID
|
||||||
from compensation.models import CompensationAction, UnitChoices, CompensationState
|
from compensation.models import CompensationAction, UnitChoices, CompensationState
|
||||||
from intervention.models import Responsibility, Legal
|
from intervention.models import Responsibility, Legal, Handler
|
||||||
from konova.models import Deadline, DeadlineType
|
from konova.models import Deadline, DeadlineType
|
||||||
from konova.utils.message_templates import DATA_UNSHARED
|
from konova.utils.message_templates import DATA_UNSHARED
|
||||||
|
|
||||||
@@ -75,7 +75,10 @@ class AbstractModelAPISerializerV1(AbstractModelAPISerializer):
|
|||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if json_str is None or len(json_str) == 0:
|
if json_str is None:
|
||||||
|
return None
|
||||||
|
json_str = str(json_str)
|
||||||
|
if len(json_str) == 0:
|
||||||
return None
|
return None
|
||||||
code = KonovaCode.objects.get(
|
code = KonovaCode.objects.get(
|
||||||
atom_id=json_str,
|
atom_id=json_str,
|
||||||
@@ -176,6 +179,12 @@ class ResponsibilityAPISerializerV1Mixin:
|
|||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
|
def _handler_to_json(self, handler: Handler):
|
||||||
|
return {
|
||||||
|
"type": self._konova_code_to_json(handler.type),
|
||||||
|
"detail": handler.detail
|
||||||
|
}
|
||||||
|
|
||||||
def _responsible_to_json(self, responsible: Responsibility):
|
def _responsible_to_json(self, responsible: Responsibility):
|
||||||
""" Serializes Responsibility model into json
|
""" Serializes Responsibility model into json
|
||||||
|
|
||||||
@@ -190,7 +199,7 @@ class ResponsibilityAPISerializerV1Mixin:
|
|||||||
"registration_file_number": responsible.registration_file_number,
|
"registration_file_number": responsible.registration_file_number,
|
||||||
"conservation_office": self._konova_code_to_json(responsible.conservation_office),
|
"conservation_office": self._konova_code_to_json(responsible.conservation_office),
|
||||||
"conservation_file_number": responsible.conservation_file_number,
|
"conservation_file_number": responsible.conservation_file_number,
|
||||||
"handler": responsible.handler,
|
"handler": self._handler_to_json(responsible.handler),
|
||||||
}
|
}
|
||||||
|
|
||||||
def _set_responsibility(self, obj, responsibility_data: dict):
|
def _set_responsibility(self, obj, responsibility_data: dict):
|
||||||
@@ -215,7 +224,11 @@ class ResponsibilityAPISerializerV1Mixin:
|
|||||||
CODELIST_CONSERVATION_OFFICE_ID,
|
CODELIST_CONSERVATION_OFFICE_ID,
|
||||||
)
|
)
|
||||||
obj.responsible.conservation_file_number = responsibility_data["conservation_file_number"]
|
obj.responsible.conservation_file_number = responsibility_data["conservation_file_number"]
|
||||||
obj.responsible.handler = responsibility_data["handler"]
|
obj.responsible.handler.type = self._konova_code_from_json(
|
||||||
|
responsibility_data["handler"]["type"],
|
||||||
|
CODELIST_HANDLER_ID,
|
||||||
|
)
|
||||||
|
obj.responsible.handler.detail = responsibility_data["handler"]["detail"]
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ class KonovaCodeAdmin(admin.ModelAdmin):
|
|||||||
"is_selectable",
|
"is_selectable",
|
||||||
"is_leaf",
|
"is_leaf",
|
||||||
"parent",
|
"parent",
|
||||||
|
"found_in_codelists",
|
||||||
]
|
]
|
||||||
|
|
||||||
search_fields = [
|
search_fields = [
|
||||||
@@ -42,6 +43,12 @@ class KonovaCodeAdmin(admin.ModelAdmin):
|
|||||||
"short_name",
|
"short_name",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def found_in_codelists(self, obj):
|
||||||
|
codelists = KonovaCodeList.objects.filter(
|
||||||
|
codes__in=[obj]
|
||||||
|
).values_list("id", flat=True)
|
||||||
|
codelists = "\n".join(str(x) for x in codelists)
|
||||||
|
return codelists
|
||||||
|
|
||||||
#admin.site.register(KonovaCodeList, KonovaCodeListAdmin)
|
#admin.site.register(KonovaCodeList, KonovaCodeListAdmin)
|
||||||
admin.site.register(KonovaCode, KonovaCodeAdmin)
|
admin.site.register(KonovaCode, KonovaCodeAdmin)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from xml.etree import ElementTree as etree
|
|||||||
|
|
||||||
from codelist.models import KonovaCode, KonovaCodeList
|
from codelist.models import KonovaCode, KonovaCodeList
|
||||||
from codelist.settings import CODELIST_INTERVENTION_HANDLER_ID, CODELIST_CONSERVATION_OFFICE_ID, \
|
from codelist.settings import CODELIST_INTERVENTION_HANDLER_ID, CODELIST_CONSERVATION_OFFICE_ID, \
|
||||||
CODELIST_REGISTRATION_OFFICE_ID, CODELIST_BIOTOPES_ID, CODELIST_LAW_ID, CODELIST_COMPENSATION_HANDLER_ID, \
|
CODELIST_REGISTRATION_OFFICE_ID, CODELIST_BIOTOPES_ID, CODELIST_LAW_ID, CODELIST_HANDLER_ID, \
|
||||||
CODELIST_COMPENSATION_ACTION_ID, CODELIST_COMPENSATION_ACTION_CLASS_ID, CODELIST_COMPENSATION_ADDITIONAL_TYPE_ID, \
|
CODELIST_COMPENSATION_ACTION_ID, CODELIST_COMPENSATION_ACTION_CLASS_ID, CODELIST_COMPENSATION_ADDITIONAL_TYPE_ID, \
|
||||||
CODELIST_BASE_URL, CODELIST_PROCESS_TYPE_ID, CODELIST_BIOTOPES_EXTRA_CODES_ID, \
|
CODELIST_BASE_URL, CODELIST_PROCESS_TYPE_ID, CODELIST_BIOTOPES_EXTRA_CODES_ID, \
|
||||||
CODELIST_COMPENSATION_ACTION_DETAIL_ID
|
CODELIST_COMPENSATION_ACTION_DETAIL_ID
|
||||||
@@ -36,7 +36,7 @@ class Command(BaseKonovaCommand):
|
|||||||
CODELIST_BIOTOPES_ID,
|
CODELIST_BIOTOPES_ID,
|
||||||
CODELIST_BIOTOPES_EXTRA_CODES_ID,
|
CODELIST_BIOTOPES_EXTRA_CODES_ID,
|
||||||
CODELIST_LAW_ID,
|
CODELIST_LAW_ID,
|
||||||
CODELIST_COMPENSATION_HANDLER_ID,
|
CODELIST_HANDLER_ID,
|
||||||
CODELIST_COMPENSATION_ACTION_ID,
|
CODELIST_COMPENSATION_ACTION_ID,
|
||||||
CODELIST_COMPENSATION_ACTION_CLASS_ID,
|
CODELIST_COMPENSATION_ACTION_CLASS_ID,
|
||||||
CODELIST_COMPENSATION_ACTION_DETAIL_ID,
|
CODELIST_COMPENSATION_ACTION_DETAIL_ID,
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
# Generated by Django 3.1.3 on 2022-01-14 08:36
|
# Generated by Django 3.1.3 on 2022-01-14 08:36
|
||||||
|
from django.core.management import call_command
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
def load_initial_codes(apps, schema_editor):
|
||||||
|
call_command('update_codelist')
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
initial = True
|
initial = True
|
||||||
@@ -32,4 +36,5 @@ class Migration(migrations.Migration):
|
|||||||
('codes', models.ManyToManyField(blank=True, help_text='Codes for this list', related_name='code_lists', to='codelist.KonovaCode')),
|
('codes', models.ManyToManyField(blank=True, help_text='Codes for this list', related_name='code_lists', to='codelist.KonovaCode')),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
migrations.RunPython(load_initial_codes),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ CODELIST_BIOTOPES_EXTRA_CODES_ID = 975 # CLZusatzbezeichnung
|
|||||||
CODELIST_LAW_ID = 1048 # CLVerfahrensrecht
|
CODELIST_LAW_ID = 1048 # CLVerfahrensrecht
|
||||||
CODELIST_PROCESS_TYPE_ID = 44382 # CLVerfahrenstyp
|
CODELIST_PROCESS_TYPE_ID = 44382 # CLVerfahrenstyp
|
||||||
|
|
||||||
CODELIST_COMPENSATION_HANDLER_ID = 1052 # CLEingreifer
|
CODELIST_HANDLER_ID = 1052 # CLEingreifer
|
||||||
CODELIST_COMPENSATION_ACTION_ID = 1026 # CLMassnahmedetail
|
CODELIST_COMPENSATION_ACTION_ID = 1026 # CLMassnahmedetail
|
||||||
CODELIST_COMPENSATION_ACTION_DETAIL_ID = 1035 # CLZusatzmerkmal
|
CODELIST_COMPENSATION_ACTION_DETAIL_ID = 1035 # CLZusatzmerkmal
|
||||||
CODELIST_COMPENSATION_ACTION_CLASS_ID = 1034 # CLMassnahmeklasse
|
CODELIST_COMPENSATION_ACTION_CLASS_ID = 1034 # CLMassnahmeklasse
|
||||||
|
|||||||
@@ -21,16 +21,30 @@ class AbstractCompensationAdmin(BaseObjectAdmin):
|
|||||||
"identifier",
|
"identifier",
|
||||||
"title",
|
"title",
|
||||||
"comment",
|
"comment",
|
||||||
"after_states",
|
"list_after_states",
|
||||||
"before_states",
|
"list_before_states",
|
||||||
|
"geometry",
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_readonly_fields(self, request, obj=None):
|
def get_readonly_fields(self, request, obj=None):
|
||||||
return super().get_readonly_fields(request, obj) + [
|
return super().get_readonly_fields(request, obj) + [
|
||||||
"after_states",
|
"list_after_states",
|
||||||
"before_states",
|
"list_before_states",
|
||||||
|
"geometry",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def list_after_states(self, obj):
|
||||||
|
states = obj.after_states.all()
|
||||||
|
states = [str(state) for state in states]
|
||||||
|
states = "\n".join(states)
|
||||||
|
return states
|
||||||
|
|
||||||
|
def list_before_states(self, obj):
|
||||||
|
states = obj.before_states.all()
|
||||||
|
states = [str(state) for state in states]
|
||||||
|
states = "\n".join(states)
|
||||||
|
return states
|
||||||
|
|
||||||
|
|
||||||
class CompensationAdmin(AbstractCompensationAdmin):
|
class CompensationAdmin(AbstractCompensationAdmin):
|
||||||
autocomplete_fields = [
|
autocomplete_fields = [
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
from codelist.models import KonovaCode
|
from codelist.models import KonovaCode
|
||||||
from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID
|
from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID, CODELIST_HANDLER_ID
|
||||||
from compensation.models import Compensation, EcoAccount
|
from compensation.models import Compensation, EcoAccount
|
||||||
from intervention.inputs import GenerateInput
|
from intervention.inputs import GenerateInput
|
||||||
from intervention.models import Intervention, Responsibility, Legal
|
from intervention.models import Intervention, Responsibility, Legal, Handler
|
||||||
from konova.forms import BaseForm, SimpleGeomForm
|
from konova.forms import BaseForm, SimpleGeomForm
|
||||||
from konova.utils.message_templates import EDITED_GENERAL_DATA, COMPENSATION_ADDED_TEMPLATE
|
from konova.utils.message_templates import EDITED_GENERAL_DATA, COMPENSATION_ADDED_TEMPLATE
|
||||||
from user.models import UserActionLogEntry
|
from user.models import UserActionLogEntry
|
||||||
@@ -101,12 +101,30 @@ class CompensationResponsibleFormMixin(forms.Form):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
handler = forms.CharField(
|
|
||||||
label=_("Eco-account handler"),
|
handler_type = forms.ModelChoiceField(
|
||||||
|
label=_("Eco-Account handler type"),
|
||||||
|
label_suffix="",
|
||||||
|
help_text=_("What type of handler is responsible for the ecoaccount?"),
|
||||||
|
required=False,
|
||||||
|
queryset=KonovaCode.objects.filter(
|
||||||
|
is_archived=False,
|
||||||
|
is_leaf=True,
|
||||||
|
code_lists__in=[CODELIST_HANDLER_ID],
|
||||||
|
),
|
||||||
|
widget=autocomplete.ModelSelect2(
|
||||||
|
url="codes-handler-autocomplete",
|
||||||
|
attrs={
|
||||||
|
"data-placeholder": _("Click for selection"),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
handler_detail = forms.CharField(
|
||||||
|
label=_("Eco-Account handler detail"),
|
||||||
label_suffix="",
|
label_suffix="",
|
||||||
max_length=255,
|
max_length=255,
|
||||||
required=False,
|
required=False,
|
||||||
help_text=_("Who handles the eco-account"),
|
help_text=_("Detail input on the handler"),
|
||||||
widget=forms.TextInput(
|
widget=forms.TextInput(
|
||||||
attrs={
|
attrs={
|
||||||
"placeholder": _("Company Mustermann"),
|
"placeholder": _("Company Mustermann"),
|
||||||
@@ -345,7 +363,8 @@ class NewEcoAccountForm(AbstractCompensationForm, CompensationResponsibleFormMix
|
|||||||
"registration_date",
|
"registration_date",
|
||||||
"surface",
|
"surface",
|
||||||
"conservation_file_number",
|
"conservation_file_number",
|
||||||
"handler",
|
"handler_type",
|
||||||
|
"handler_detail",
|
||||||
"comment",
|
"comment",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -368,7 +387,8 @@ class NewEcoAccountForm(AbstractCompensationForm, CompensationResponsibleFormMix
|
|||||||
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)
|
||||||
registration_date = self.cleaned_data.get("registration_date", None)
|
registration_date = self.cleaned_data.get("registration_date", None)
|
||||||
handler = self.cleaned_data.get("handler", None)
|
handler_type = self.cleaned_data.get("handler_type", None)
|
||||||
|
handler_detail = self.cleaned_data.get("handler_detail", None)
|
||||||
surface = self.cleaned_data.get("surface", None)
|
surface = self.cleaned_data.get("surface", None)
|
||||||
conservation_office = self.cleaned_data.get("conservation_office", None)
|
conservation_office = self.cleaned_data.get("conservation_office", None)
|
||||||
conservation_file_number = self.cleaned_data.get("conservation_file_number", None)
|
conservation_file_number = self.cleaned_data.get("conservation_file_number", None)
|
||||||
@@ -379,6 +399,11 @@ class NewEcoAccountForm(AbstractCompensationForm, CompensationResponsibleFormMix
|
|||||||
# Process the geometry form
|
# Process the geometry form
|
||||||
geometry = geom_form.save(action)
|
geometry = geom_form.save(action)
|
||||||
|
|
||||||
|
handler = Handler.objects.create(
|
||||||
|
type=handler_type,
|
||||||
|
detail=handler_detail,
|
||||||
|
)
|
||||||
|
|
||||||
responsible = Responsibility.objects.create(
|
responsible = Responsibility.objects.create(
|
||||||
handler=handler,
|
handler=handler,
|
||||||
conservation_file_number=conservation_file_number,
|
conservation_file_number=conservation_file_number,
|
||||||
@@ -423,11 +448,13 @@ class EditEcoAccountForm(NewEcoAccountForm):
|
|||||||
reg_date = self.instance.legal.registration_date
|
reg_date = self.instance.legal.registration_date
|
||||||
if reg_date is not None:
|
if reg_date is not None:
|
||||||
reg_date = reg_date.isoformat()
|
reg_date = reg_date.isoformat()
|
||||||
|
|
||||||
form_data = {
|
form_data = {
|
||||||
"identifier": self.instance.identifier,
|
"identifier": self.instance.identifier,
|
||||||
"title": self.instance.title,
|
"title": self.instance.title,
|
||||||
"surface": self.instance.deductable_surface,
|
"surface": self.instance.deductable_surface,
|
||||||
"handler": self.instance.responsible.handler,
|
"handler_type": self.instance.responsible.handler.type,
|
||||||
|
"handler_detail": self.instance.responsible.handler.detail,
|
||||||
"registration_date": reg_date,
|
"registration_date": reg_date,
|
||||||
"conservation_office": self.instance.responsible.conservation_office,
|
"conservation_office": self.instance.responsible.conservation_office,
|
||||||
"conservation_file_number": self.instance.responsible.conservation_file_number,
|
"conservation_file_number": self.instance.responsible.conservation_file_number,
|
||||||
@@ -445,7 +472,8 @@ class EditEcoAccountForm(NewEcoAccountForm):
|
|||||||
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)
|
||||||
registration_date = self.cleaned_data.get("registration_date", None)
|
registration_date = self.cleaned_data.get("registration_date", None)
|
||||||
handler = self.cleaned_data.get("handler", None)
|
handler_type = self.cleaned_data.get("handler_type", None)
|
||||||
|
handler_detail = self.cleaned_data.get("handler_detail", None)
|
||||||
surface = self.cleaned_data.get("surface", None)
|
surface = self.cleaned_data.get("surface", None)
|
||||||
conservation_office = self.cleaned_data.get("conservation_office", None)
|
conservation_office = self.cleaned_data.get("conservation_office", None)
|
||||||
conservation_file_number = self.cleaned_data.get("conservation_file_number", None)
|
conservation_file_number = self.cleaned_data.get("conservation_file_number", None)
|
||||||
@@ -458,7 +486,9 @@ class EditEcoAccountForm(NewEcoAccountForm):
|
|||||||
geometry = geom_form.save(action)
|
geometry = geom_form.save(action)
|
||||||
|
|
||||||
# Update responsible data
|
# Update responsible data
|
||||||
self.instance.responsible.handler = handler
|
self.instance.responsible.handler.type = handler_type
|
||||||
|
self.instance.responsible.handler.detail = handler_detail
|
||||||
|
self.instance.responsible.handler.save()
|
||||||
self.instance.responsible.conservation_office = conservation_office
|
self.instance.responsible.conservation_office = conservation_office
|
||||||
self.instance.responsible.conservation_file_number = conservation_file_number
|
self.instance.responsible.conservation_file_number = conservation_file_number
|
||||||
self.instance.responsible.save()
|
self.instance.responsible.save()
|
||||||
|
|||||||
@@ -128,6 +128,7 @@ class EditPaymentModalForm(NewPaymentForm):
|
|||||||
payment.comment = self.cleaned_data.get("comment", None)
|
payment.comment = self.cleaned_data.get("comment", None)
|
||||||
payment.save()
|
payment.save()
|
||||||
self.instance.mark_as_edited(self.user, self.request, edit_comment=PAYMENT_EDITED)
|
self.instance.mark_as_edited(self.user, self.request, edit_comment=PAYMENT_EDITED)
|
||||||
|
self.instance.send_data_to_egon()
|
||||||
return payment
|
return payment
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -418,6 +418,18 @@ class Compensation(AbstractCompensation, CEFMixin, CoherenceMixin):
|
|||||||
super().set_status_messages(request)
|
super().set_status_messages(request)
|
||||||
return request
|
return request
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_recorded(self):
|
||||||
|
""" Getter for record status as property
|
||||||
|
|
||||||
|
Since compensations inherit their record status from their intervention, the intervention's status is being
|
||||||
|
returned
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
return self.intervention.is_recorded
|
||||||
|
|
||||||
|
|
||||||
class CompensationDocument(AbstractDocument):
|
class CompensationDocument(AbstractDocument):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ class CompensationTable(BaseTable, TableRenderMixin):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
parcels = value.get_underlying_parcels().values_list(
|
parcels = value.get_underlying_parcels().values_list(
|
||||||
"gmrkng",
|
"parcel_group__name",
|
||||||
flat=True
|
flat=True
|
||||||
).distinct()
|
).distinct()
|
||||||
html = render_to_string(
|
html = render_to_string(
|
||||||
@@ -181,9 +181,7 @@ class CompensationTable(BaseTable, TableRenderMixin):
|
|||||||
"""
|
"""
|
||||||
if value is None:
|
if value is None:
|
||||||
value = User.objects.none()
|
value = User.objects.none()
|
||||||
has_access = value.filter(
|
has_access = record.is_shared_with(self.user)
|
||||||
id=self.user.id
|
|
||||||
).exists()
|
|
||||||
|
|
||||||
html = self.render_icn(
|
html = self.render_icn(
|
||||||
tooltip=_("Full access granted") if has_access else _("Access not granted"),
|
tooltip=_("Full access granted") if has_access else _("Access not granted"),
|
||||||
@@ -295,7 +293,7 @@ class EcoAccountTable(BaseTable, TableRenderMixin):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
parcels = value.get_underlying_parcels().values_list(
|
parcels = value.get_underlying_parcels().values_list(
|
||||||
"gmrkng",
|
"parcel_group__name",
|
||||||
flat=True
|
flat=True
|
||||||
).distinct()
|
).distinct()
|
||||||
html = render_to_string(
|
html = render_to_string(
|
||||||
@@ -343,7 +341,7 @@ class EcoAccountTable(BaseTable, TableRenderMixin):
|
|||||||
html = ""
|
html = ""
|
||||||
# Do not use value in here, since value does use unprefetched 'users' manager, where record has already
|
# Do not use value in here, since value does use unprefetched 'users' manager, where record has already
|
||||||
# prefetched users data
|
# prefetched users data
|
||||||
has_access = self.user in record.users.all()
|
has_access = record.is_shared_with(self.user)
|
||||||
html += self.render_icn(
|
html += self.render_icn(
|
||||||
tooltip=_("Full access granted") if has_access else _("Access not granted"),
|
tooltip=_("Full access granted") if has_access else _("Access not granted"),
|
||||||
icn_class="fas fa-edit rlp-r-inv" if has_access else "far fa-edit",
|
icn_class="fas fa-edit rlp-r-inv" if has_access else "far fa-edit",
|
||||||
|
|||||||
@@ -90,9 +90,15 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans 'Last modified' %}</th>
|
<th scope="row">{% trans 'Last modified' %}</th>
|
||||||
<td class="align-middle">
|
<td class="align-middle">
|
||||||
{{obj.modified.timestamp|default_if_none:""|naturalday}}
|
{% if obj.modified %}
|
||||||
<br>
|
{{obj.modified.timestamp|default_if_none:""}}
|
||||||
{{obj.modified.user.username}}
|
<br>
|
||||||
|
{{obj.modified.user.username}}
|
||||||
|
{% else %}
|
||||||
|
{{obj.created.timestamp|default_if_none:""}}
|
||||||
|
<br>
|
||||||
|
{{obj.created.user.username}}
|
||||||
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -116,7 +122,7 @@
|
|||||||
{% include 'map/geom_form.html' %}
|
{% include 'map/geom_form.html' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% include 'konova/includes/parcels.html' %}
|
{% include 'konova/includes/parcels/parcels.html' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% include 'konova/includes/comment_card.html' %}
|
{% include 'konova/includes/comment_card.html' %}
|
||||||
|
|||||||
@@ -73,9 +73,15 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans 'Last modified' %}</th>
|
<th scope="row">{% trans 'Last modified' %}</th>
|
||||||
<td class="align-middle">
|
<td class="align-middle">
|
||||||
{{obj.modified.timestamp|default_if_none:""|naturalday}}
|
{% if obj.modified %}
|
||||||
<br>
|
{{obj.modified.timestamp|default_if_none:""}}
|
||||||
{{obj.modified.user.username}}
|
<br>
|
||||||
|
{{obj.modified.user.username}}
|
||||||
|
{% else %}
|
||||||
|
{{obj.created.timestamp|default_if_none:""}}
|
||||||
|
<br>
|
||||||
|
{{obj.created.user.username}}
|
||||||
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -98,7 +104,7 @@
|
|||||||
{% include 'map/geom_form.html' %}
|
{% include 'map/geom_form.html' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% include 'konova/includes/parcels.html' %}
|
{% include 'konova/includes/parcels/parcels.html' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% include 'konova/includes/comment_card.html' %}
|
{% include 'konova/includes/comment_card.html' %}
|
||||||
|
|||||||
@@ -38,17 +38,10 @@
|
|||||||
{% include 'map/geom_form.html' %}
|
{% include 'map/geom_form.html' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% include 'konova/includes/parcels.html' %}
|
{% include 'konova/includes/parcels/parcels.html' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-6 col-md-6 col-lg-6">
|
{% include 'konova/includes/report/qrcodes.html' %}
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -51,17 +51,10 @@
|
|||||||
{% include 'map/geom_form.html' %}
|
{% include 'map/geom_form.html' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% include 'konova/includes/parcels.html' %}
|
{% include 'konova/includes/parcels/parcels.html' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-6 col-md-6 col-lg-6">
|
{% include 'konova/includes/report/qrcodes.html' %}
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -60,8 +60,9 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase):
|
|||||||
|
|
||||||
# Preserve the current number of intervention's compensations
|
# Preserve the current number of intervention's compensations
|
||||||
num_compensations = self.intervention.compensations.count()
|
num_compensations = self.intervention.compensations.count()
|
||||||
self.client_user.post(new_url, post_data)
|
response = self.client_user.post(new_url, post_data)
|
||||||
|
|
||||||
|
self.assertEqual(302, response.status_code)
|
||||||
self.intervention.refresh_from_db()
|
self.intervention.refresh_from_db()
|
||||||
self.assertEqual(num_compensations + 1, self.intervention.compensations.count())
|
self.assertEqual(num_compensations + 1, self.intervention.compensations.count())
|
||||||
new_compensation = self.intervention.compensations.get(identifier=test_id)
|
new_compensation = self.intervention.compensations.get(identifier=test_id)
|
||||||
@@ -261,3 +262,26 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase):
|
|||||||
self.assertIn(recorded, self.compensation.log.all())
|
self.assertIn(recorded, self.compensation.log.all())
|
||||||
self.assertEqual(pre_record_log_count + 1, self.compensation.log.count())
|
self.assertEqual(pre_record_log_count + 1, self.compensation.log.count())
|
||||||
|
|
||||||
|
def test_non_editable_after_recording(self):
|
||||||
|
""" Tests that the compensation can not be edited after being recorded
|
||||||
|
|
||||||
|
User must be redirected to another page
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.assertIsNotNone(self.compensation)
|
||||||
|
self.assertFalse(self.compensation.is_recorded)
|
||||||
|
edit_url = reverse("compensation:edit", args=(self.compensation.id,))
|
||||||
|
response = self.client_user.get(edit_url)
|
||||||
|
has_redirect = response.status_code == 302
|
||||||
|
self.assertFalse(has_redirect)
|
||||||
|
|
||||||
|
self.compensation.intervention.set_recorded(self.user)
|
||||||
|
self.assertTrue(self.compensation.is_recorded)
|
||||||
|
|
||||||
|
edit_url = reverse("compensation:edit", args=(self.compensation.id,))
|
||||||
|
response = self.client_user.get(edit_url)
|
||||||
|
has_redirect = response.status_code == 302
|
||||||
|
self.assertTrue(has_redirect)
|
||||||
|
self.compensation.intervention.set_unrecorded(self.user)
|
||||||
|
|||||||
@@ -302,3 +302,27 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase):
|
|||||||
self.assertEqual(pre_edit_account_log_count + 1, account.log.count())
|
self.assertEqual(pre_edit_account_log_count + 1, account.log.count())
|
||||||
self.assertEqual(intervention.log.first().action, UserAction.EDITED)
|
self.assertEqual(intervention.log.first().action, UserAction.EDITED)
|
||||||
self.assertEqual(account.log.first().action, UserAction.EDITED)
|
self.assertEqual(account.log.first().action, UserAction.EDITED)
|
||||||
|
|
||||||
|
def test_non_editable_after_recording(self):
|
||||||
|
""" Tests that the eco_account can not be edited after being recorded
|
||||||
|
|
||||||
|
User must be redirected to another page
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.assertIsNotNone(self.eco_account)
|
||||||
|
self.assertFalse(self.eco_account.is_recorded)
|
||||||
|
edit_url = reverse("compensation:acc:edit", args=(self.eco_account.id,))
|
||||||
|
response = self.client_user.get(edit_url)
|
||||||
|
has_redirect = response.status_code == 302
|
||||||
|
self.assertFalse(has_redirect)
|
||||||
|
|
||||||
|
self.eco_account.set_recorded(self.user)
|
||||||
|
self.assertTrue(self.eco_account.is_recorded)
|
||||||
|
|
||||||
|
edit_url = reverse("compensation:acc:edit", args=(self.eco_account.id,))
|
||||||
|
response = self.client_user.get(edit_url)
|
||||||
|
has_redirect = response.status_code == 302
|
||||||
|
self.assertTrue(has_redirect)
|
||||||
|
self.eco_account.set_unrecorded(self.user)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.db.models import Sum
|
from django.db.models import Sum
|
||||||
from django.http import HttpRequest, JsonResponse
|
from django.http import HttpRequest, JsonResponse
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
@@ -22,7 +23,7 @@ from konova.utils.message_templates import FORM_INVALID, IDENTIFIER_REPLACED, DA
|
|||||||
CHECKED_RECORDED_RESET, COMPENSATION_ADDED_TEMPLATE, COMPENSATION_REMOVED_TEMPLATE, DOCUMENT_ADDED, \
|
CHECKED_RECORDED_RESET, COMPENSATION_ADDED_TEMPLATE, COMPENSATION_REMOVED_TEMPLATE, DOCUMENT_ADDED, \
|
||||||
COMPENSATION_STATE_REMOVED, COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_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_ADDED, DEADLINE_REMOVED, DOCUMENT_EDITED, COMPENSATION_STATE_EDITED, COMPENSATION_ACTION_EDITED, \
|
||||||
DEADLINE_EDITED
|
DEADLINE_EDITED, RECORDED_BLOCKS_EDIT, PARAMS_INVALID
|
||||||
from konova.utils.user_checks import in_group
|
from konova.utils.user_checks import in_group
|
||||||
|
|
||||||
|
|
||||||
@@ -69,6 +70,19 @@ def new_view(request: HttpRequest, intervention_id: str = None):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
template = "compensation/form/view.html"
|
template = "compensation/form/view.html"
|
||||||
|
if intervention_id is not None:
|
||||||
|
try:
|
||||||
|
intervention = Intervention.objects.get(id=intervention_id)
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
messages.error(request, PARAMS_INVALID)
|
||||||
|
return redirect("home")
|
||||||
|
if intervention.is_recorded:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
RECORDED_BLOCKS_EDIT
|
||||||
|
)
|
||||||
|
return redirect("intervention:detail", id=intervention_id)
|
||||||
|
|
||||||
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":
|
||||||
@@ -134,6 +148,13 @@ def edit_view(request: HttpRequest, id: str):
|
|||||||
template = "compensation/form/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)
|
||||||
|
if comp.is_recorded:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
RECORDED_BLOCKS_EDIT
|
||||||
|
)
|
||||||
|
return redirect("compensation:detail", id=id)
|
||||||
|
|
||||||
# Create forms, initialize with values from db/from POST request
|
# Create forms, initialize with values from db/from POST request
|
||||||
data_form = EditCompensationForm(request.POST or None, instance=comp)
|
data_form = EditCompensationForm(request.POST or None, instance=comp)
|
||||||
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=comp)
|
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=comp)
|
||||||
@@ -596,14 +617,12 @@ def report_view(request: HttpRequest, id: str):
|
|||||||
instance=comp
|
instance=comp
|
||||||
)
|
)
|
||||||
parcels = comp.get_underlying_parcels()
|
parcels = comp.get_underlying_parcels()
|
||||||
qrcode_img = generate_qr_code(
|
|
||||||
request.build_absolute_uri(reverse("compensation:report", args=(id,))),
|
qrcode_url = request.build_absolute_uri(reverse("compensation:report", args=(id,)))
|
||||||
10
|
qrcode_img = generate_qr_code(qrcode_url, 10)
|
||||||
)
|
qrcode_lanis_url = comp.get_LANIS_link()
|
||||||
qrcode_img_lanis = generate_qr_code(
|
qrcode_img_lanis = generate_qr_code(qrcode_lanis_url, 7)
|
||||||
comp.get_LANIS_link(),
|
|
||||||
7
|
|
||||||
)
|
|
||||||
# Order states by surface
|
# Order states by surface
|
||||||
before_states = comp.before_states.all().order_by("-surface").prefetch_related("biotope_type")
|
before_states = comp.before_states.all().order_by("-surface").prefetch_related("biotope_type")
|
||||||
after_states = comp.after_states.all().order_by("-surface").prefetch_related("biotope_type")
|
after_states = comp.after_states.all().order_by("-surface").prefetch_related("biotope_type")
|
||||||
@@ -611,8 +630,14 @@ def report_view(request: HttpRequest, id: str):
|
|||||||
|
|
||||||
context = {
|
context = {
|
||||||
"obj": comp,
|
"obj": comp,
|
||||||
"qrcode": qrcode_img,
|
"qrcode": {
|
||||||
"qrcode_lanis": qrcode_img_lanis,
|
"img": qrcode_img,
|
||||||
|
"url": qrcode_url,
|
||||||
|
},
|
||||||
|
"qrcode_lanis": {
|
||||||
|
"img": qrcode_img_lanis,
|
||||||
|
"url": qrcode_lanis_url,
|
||||||
|
},
|
||||||
"has_access": False, # disables action buttons during rendering
|
"has_access": False, # disables action buttons during rendering
|
||||||
"before_states": before_states,
|
"before_states": before_states,
|
||||||
"after_states": after_states,
|
"after_states": after_states,
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ from konova.utils.generators import generate_qr_code
|
|||||||
from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID, DATA_UNSHARED, DATA_UNSHARED_EXPLANATION, \
|
from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID, DATA_UNSHARED, DATA_UNSHARED_EXPLANATION, \
|
||||||
CANCEL_ACC_RECORDED_OR_DEDUCTED, DEDUCTION_REMOVED, DEDUCTION_ADDED, DOCUMENT_ADDED, COMPENSATION_STATE_REMOVED, \
|
CANCEL_ACC_RECORDED_OR_DEDUCTED, DEDUCTION_REMOVED, DEDUCTION_ADDED, DOCUMENT_ADDED, COMPENSATION_STATE_REMOVED, \
|
||||||
COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_ADDED, DEADLINE_ADDED, DEADLINE_REMOVED, \
|
COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_ADDED, DEADLINE_ADDED, DEADLINE_REMOVED, \
|
||||||
DEDUCTION_EDITED, DOCUMENT_EDITED, COMPENSATION_STATE_EDITED, COMPENSATION_ACTION_EDITED, DEADLINE_EDITED
|
DEDUCTION_EDITED, DOCUMENT_EDITED, COMPENSATION_STATE_EDITED, COMPENSATION_ACTION_EDITED, DEADLINE_EDITED, \
|
||||||
|
RECORDED_BLOCKS_EDIT
|
||||||
from konova.utils.user_checks import in_group
|
from konova.utils.user_checks import in_group
|
||||||
|
|
||||||
|
|
||||||
@@ -145,6 +146,13 @@ def edit_view(request: HttpRequest, id: str):
|
|||||||
template = "compensation/form/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)
|
||||||
|
if acc.is_recorded:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
RECORDED_BLOCKS_EDIT
|
||||||
|
)
|
||||||
|
return redirect("compensation:acc:detail", id=id)
|
||||||
|
|
||||||
# Create forms, initialize with values from db/from POST request
|
# Create forms, initialize with values from db/from POST request
|
||||||
data_form = EditEcoAccountForm(request.POST or None, instance=acc)
|
data_form = EditEcoAccountForm(request.POST or None, instance=acc)
|
||||||
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=acc)
|
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=acc)
|
||||||
@@ -731,18 +739,16 @@ def report_view(request:HttpRequest, id: str):
|
|||||||
instance=acc
|
instance=acc
|
||||||
)
|
)
|
||||||
parcels = acc.get_underlying_parcels()
|
parcels = acc.get_underlying_parcels()
|
||||||
qrcode_img = generate_qr_code(
|
|
||||||
request.build_absolute_uri(reverse("ema:report", args=(id,))),
|
qrcode_url = request.build_absolute_uri(reverse("ema:report", args=(id,)))
|
||||||
10
|
qrcode_img = generate_qr_code(qrcode_url, 10)
|
||||||
)
|
qrcode_lanis_url = acc.get_LANIS_link()
|
||||||
qrcode_img_lanis = generate_qr_code(
|
qrcode_img_lanis = generate_qr_code(qrcode_lanis_url, 7)
|
||||||
acc.get_LANIS_link(),
|
|
||||||
7
|
|
||||||
)
|
|
||||||
# Order states by surface
|
# Order states by surface
|
||||||
before_states = acc.before_states.all().order_by("-surface").select_related("biotope_type__parent")
|
before_states = acc.before_states.all().order_by("-surface").select_related("biotope_type__parent")
|
||||||
after_states = acc.after_states.all().order_by("-surface").select_related("biotope_type__parent")
|
after_states = acc.after_states.all().order_by("-surface").select_related("biotope_type__parent")
|
||||||
actions = acc.actions.all().select_related("action_type__parent")
|
actions = acc.actions.all().prefetch_related("action_type__parent")
|
||||||
|
|
||||||
# Reduce amount of db fetched data to the bare minimum we need in the template (deduction's intervention id and identifier)
|
# Reduce amount of db fetched data to the bare minimum we need in the template (deduction's intervention id and identifier)
|
||||||
deductions = acc.deductions.all()\
|
deductions = acc.deductions.all()\
|
||||||
@@ -752,8 +758,14 @@ def report_view(request:HttpRequest, id: str):
|
|||||||
|
|
||||||
context = {
|
context = {
|
||||||
"obj": acc,
|
"obj": acc,
|
||||||
"qrcode": qrcode_img,
|
"qrcode": {
|
||||||
"qrcode_lanis": qrcode_img_lanis,
|
"img": qrcode_img,
|
||||||
|
"url": qrcode_url,
|
||||||
|
},
|
||||||
|
"qrcode_lanis": {
|
||||||
|
"img": qrcode_img_lanis,
|
||||||
|
"url": qrcode_lanis_url,
|
||||||
|
},
|
||||||
"has_access": False, # disables action buttons during rendering
|
"has_access": False, # disables action buttons during rendering
|
||||||
"before_states": before_states,
|
"before_states": before_states,
|
||||||
"after_states": after_states,
|
"after_states": after_states,
|
||||||
|
|||||||
22
ema/forms.py
22
ema/forms.py
@@ -14,7 +14,7 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
|
|
||||||
from compensation.forms.forms import AbstractCompensationForm, CompensationResponsibleFormMixin
|
from compensation.forms.forms import AbstractCompensationForm, CompensationResponsibleFormMixin
|
||||||
from ema.models import Ema, EmaDocument
|
from ema.models import Ema, EmaDocument
|
||||||
from intervention.models import Responsibility
|
from intervention.models import Responsibility, Handler
|
||||||
from konova.forms import SimpleGeomForm, NewDocumentModalForm
|
from konova.forms import SimpleGeomForm, NewDocumentModalForm
|
||||||
from user.models import UserActionLogEntry
|
from user.models import UserActionLogEntry
|
||||||
|
|
||||||
@@ -31,7 +31,8 @@ class NewEmaForm(AbstractCompensationForm, CompensationResponsibleFormMixin):
|
|||||||
"title",
|
"title",
|
||||||
"conservation_office",
|
"conservation_office",
|
||||||
"conservation_file_number",
|
"conservation_file_number",
|
||||||
"handler",
|
"handler_type",
|
||||||
|
"handler_detail",
|
||||||
"comment",
|
"comment",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -53,7 +54,8 @@ class NewEmaForm(AbstractCompensationForm, CompensationResponsibleFormMixin):
|
|||||||
# 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)
|
||||||
handler = self.cleaned_data.get("handler", None)
|
handler_type = self.cleaned_data.get("handler_type", None)
|
||||||
|
handler_detail = self.cleaned_data.get("handler_detail", None)
|
||||||
conservation_office = self.cleaned_data.get("conservation_office", None)
|
conservation_office = self.cleaned_data.get("conservation_office", None)
|
||||||
conservation_file_number = self.cleaned_data.get("conservation_file_number", None)
|
conservation_file_number = self.cleaned_data.get("conservation_file_number", None)
|
||||||
comment = self.cleaned_data.get("comment", None)
|
comment = self.cleaned_data.get("comment", None)
|
||||||
@@ -63,6 +65,10 @@ class NewEmaForm(AbstractCompensationForm, CompensationResponsibleFormMixin):
|
|||||||
# Process the geometry form
|
# Process the geometry form
|
||||||
geometry = geom_form.save(action)
|
geometry = geom_form.save(action)
|
||||||
|
|
||||||
|
handler = Handler.objects.create(
|
||||||
|
type=handler_type,
|
||||||
|
detail=handler_detail
|
||||||
|
)
|
||||||
responsible = Responsibility.objects.create(
|
responsible = Responsibility.objects.create(
|
||||||
handler=handler,
|
handler=handler,
|
||||||
conservation_file_number=conservation_file_number,
|
conservation_file_number=conservation_file_number,
|
||||||
@@ -105,7 +111,8 @@ class EditEmaForm(NewEmaForm):
|
|||||||
form_data = {
|
form_data = {
|
||||||
"identifier": self.instance.identifier,
|
"identifier": self.instance.identifier,
|
||||||
"title": self.instance.title,
|
"title": self.instance.title,
|
||||||
"handler": self.instance.responsible.handler,
|
"handler_type": self.instance.responsible.handler.type,
|
||||||
|
"handler_detail": self.instance.responsible.handler.detail,
|
||||||
"conservation_office": self.instance.responsible.conservation_office,
|
"conservation_office": self.instance.responsible.conservation_office,
|
||||||
"conservation_file_number": self.instance.responsible.conservation_file_number,
|
"conservation_file_number": self.instance.responsible.conservation_file_number,
|
||||||
"comment": self.instance.comment,
|
"comment": self.instance.comment,
|
||||||
@@ -121,7 +128,8 @@ class EditEmaForm(NewEmaForm):
|
|||||||
# 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)
|
||||||
handler = self.cleaned_data.get("handler", None)
|
handler_type = self.cleaned_data.get("handler_type", None)
|
||||||
|
handler_detail = self.cleaned_data.get("handler_detail", None)
|
||||||
conservation_office = self.cleaned_data.get("conservation_office", None)
|
conservation_office = self.cleaned_data.get("conservation_office", None)
|
||||||
conservation_file_number = self.cleaned_data.get("conservation_file_number", None)
|
conservation_file_number = self.cleaned_data.get("conservation_file_number", None)
|
||||||
comment = self.cleaned_data.get("comment", None)
|
comment = self.cleaned_data.get("comment", None)
|
||||||
@@ -132,7 +140,9 @@ class EditEmaForm(NewEmaForm):
|
|||||||
geometry = geom_form.save(action)
|
geometry = geom_form.save(action)
|
||||||
|
|
||||||
# Update responsible data
|
# Update responsible data
|
||||||
self.instance.responsible.handler = handler
|
self.instance.responsible.handler.type = handler_type
|
||||||
|
self.instance.responsible.handler.detail = handler_detail
|
||||||
|
self.instance.responsible.handler.save()
|
||||||
self.instance.responsible.conservation_office = conservation_office
|
self.instance.responsible.conservation_office = conservation_office
|
||||||
self.instance.responsible.conservation_file_number = conservation_file_number
|
self.instance.responsible.conservation_file_number = conservation_file_number
|
||||||
self.instance.responsible.save()
|
self.instance.responsible.save()
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ class EmaTable(BaseTable, TableRenderMixin):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
parcels = value.get_underlying_parcels().values_list(
|
parcels = value.get_underlying_parcels().values_list(
|
||||||
"gmrkng",
|
"parcel_group__name",
|
||||||
flat=True
|
flat=True
|
||||||
).distinct()
|
).distinct()
|
||||||
html = render_to_string(
|
html = render_to_string(
|
||||||
@@ -151,9 +151,7 @@ class EmaTable(BaseTable, TableRenderMixin):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
html = ""
|
html = ""
|
||||||
has_access = value.filter(
|
has_access = record.is_shared_with(self.user)
|
||||||
id=self.user.id
|
|
||||||
).exists()
|
|
||||||
|
|
||||||
html += self.render_icn(
|
html += self.render_icn(
|
||||||
tooltip=_("Full access granted") if has_access else _("Access not granted"),
|
tooltip=_("Full access granted") if has_access else _("Access not granted"),
|
||||||
|
|||||||
@@ -60,14 +60,13 @@
|
|||||||
<th scope="row">{% trans 'Last modified' %}</th>
|
<th scope="row">{% trans 'Last modified' %}</th>
|
||||||
<td class="align-middle">
|
<td class="align-middle">
|
||||||
{% if obj.modified %}
|
{% if obj.modified %}
|
||||||
{{obj.modified.timestamp|default_if_none:""|naturalday}}
|
{{obj.modified.timestamp|default_if_none:""}}
|
||||||
<br>
|
<br>
|
||||||
{{obj.modified.user.username}}
|
{{obj.modified.user.username}}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{obj.created.timestamp|default_if_none:""|naturalday}}
|
{{obj.created.timestamp|default_if_none:""}}
|
||||||
<br>
|
<br>
|
||||||
{{obj.created.user.username}}
|
{{obj.created.user.username}}
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -91,7 +90,7 @@
|
|||||||
{% include 'map/geom_form.html' %}
|
{% include 'map/geom_form.html' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% include 'konova/includes/parcels.html' %}
|
{% include 'konova/includes/parcels/parcels.html' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% include 'konova/includes/comment_card.html' %}
|
{% include 'konova/includes/comment_card.html' %}
|
||||||
|
|||||||
@@ -38,17 +38,10 @@
|
|||||||
{% include 'map/geom_form.html' %}
|
{% include 'map/geom_form.html' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% include 'konova/includes/parcels.html' %}
|
{% include 'konova/includes/parcels/parcels.html' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-6 col-md-6 col-lg-6">
|
{% include 'konova/includes/report/qrcodes.html' %}
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -79,7 +79,9 @@ class EmaViewTestCase(CompensationViewTestCase):
|
|||||||
# Create log entry
|
# Create log entry
|
||||||
action = UserActionLogEntry.get_created_action(self.superuser)
|
action = UserActionLogEntry.get_created_action(self.superuser)
|
||||||
# Create responsible data object
|
# Create responsible data object
|
||||||
responsibility_data = Responsibility.objects.create()
|
responsibility_data = Responsibility.objects.create(
|
||||||
|
handler=self.handler
|
||||||
|
)
|
||||||
geometry = Geometry.objects.create()
|
geometry = Geometry.objects.create()
|
||||||
self.ema = Ema.objects.create(
|
self.ema = Ema.objects.create(
|
||||||
identifier="TEST",
|
identifier="TEST",
|
||||||
|
|||||||
@@ -117,6 +117,32 @@ class EmaWorkflowTestCase(BaseWorkflowTestCase):
|
|||||||
self.assertEqual(pre_edit_log_count + 1, self.ema.log.count())
|
self.assertEqual(pre_edit_log_count + 1, self.ema.log.count())
|
||||||
self.assertEqual(self.ema.log.first().action, UserAction.EDITED)
|
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):
|
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)
|
This tests if the recordability of the Ema is triggered by the quality of it's data (e.g. not all fields filled)
|
||||||
|
|||||||
33
ema/views.py
33
ema/views.py
@@ -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, \
|
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, \
|
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_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
|
from konova.utils.user_checks import in_group
|
||||||
|
|
||||||
|
|
||||||
@@ -213,6 +213,13 @@ def edit_view(request: HttpRequest, id: str):
|
|||||||
template = "compensation/form/view.html"
|
template = "compensation/form/view.html"
|
||||||
# Get object from db
|
# Get object from db
|
||||||
ema = get_object_or_404(Ema, id=id)
|
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
|
# Create forms, initialize with values from db/from POST request
|
||||||
data_form = EditEmaForm(request.POST or None, instance=ema)
|
data_form = EditEmaForm(request.POST or None, instance=ema)
|
||||||
geom_form = SimpleGeomForm(request.POST or None, read_only=False, 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,
|
instance=ema,
|
||||||
)
|
)
|
||||||
parcels = ema.get_underlying_parcels()
|
parcels = ema.get_underlying_parcels()
|
||||||
qrcode_img = generate_qr_code(
|
|
||||||
request.build_absolute_uri(reverse("ema:report", args=(id,))),
|
qrcode_url = request.build_absolute_uri(reverse("ema:report", args=(id,)))
|
||||||
10
|
qrcode_img = generate_qr_code(qrcode_url, 10)
|
||||||
)
|
qrcode_lanis_url = ema.get_LANIS_link()
|
||||||
qrcode_img_lanis = generate_qr_code(
|
qrcode_img_lanis = generate_qr_code(qrcode_lanis_url, 7)
|
||||||
ema.get_LANIS_link(),
|
|
||||||
7
|
|
||||||
)
|
|
||||||
# Order states by surface
|
# Order states by surface
|
||||||
before_states = ema.before_states.all().order_by("-surface").prefetch_related("biotope_type")
|
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")
|
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 = {
|
context = {
|
||||||
"obj": ema,
|
"obj": ema,
|
||||||
"qrcode": qrcode_img,
|
"qrcode": {
|
||||||
"qrcode_lanis": qrcode_img_lanis,
|
"img": qrcode_img,
|
||||||
|
"url": qrcode_url
|
||||||
|
},
|
||||||
|
"qrcode_lanis": {
|
||||||
|
"img": qrcode_img_lanis,
|
||||||
|
"url": qrcode_lanis_url
|
||||||
|
},
|
||||||
"has_access": False, # disables action buttons during rendering
|
"has_access": False, # disables action buttons during rendering
|
||||||
"before_states": before_states,
|
"before_states": before_states,
|
||||||
"after_states": after_states,
|
"after_states": after_states,
|
||||||
|
|||||||
@@ -25,12 +25,14 @@ class InterventionAdmin(BaseObjectAdmin):
|
|||||||
"checked",
|
"checked",
|
||||||
"recorded",
|
"recorded",
|
||||||
"users",
|
"users",
|
||||||
|
"geometry",
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_readonly_fields(self, request, obj=None):
|
def get_readonly_fields(self, request, obj=None):
|
||||||
return super().get_readonly_fields(request, obj) + [
|
return super().get_readonly_fields(request, obj) + [
|
||||||
"checked",
|
"checked",
|
||||||
"recorded",
|
"recorded",
|
||||||
|
"geometry",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
|
|
||||||
from codelist.models import KonovaCode
|
from codelist.models import KonovaCode
|
||||||
from codelist.settings import 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
|
CODELIST_REGISTRATION_OFFICE_ID, CODELIST_CONSERVATION_OFFICE_ID, CODELIST_HANDLER_ID
|
||||||
from intervention.inputs import GenerateInput
|
from intervention.inputs import GenerateInput
|
||||||
from intervention.models import Intervention, Legal, Responsibility
|
from intervention.models import Intervention, Legal, Responsibility, Handler
|
||||||
from konova.forms import BaseForm, SimpleGeomForm
|
from konova.forms import BaseForm, SimpleGeomForm
|
||||||
from user.models import UserActionLogEntry
|
from user.models import UserActionLogEntry
|
||||||
|
|
||||||
@@ -138,12 +138,29 @@ class NewInterventionForm(BaseForm):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
handler = forms.CharField(
|
handler_type = forms.ModelChoiceField(
|
||||||
label=_("Intervention handler"),
|
label=_("Intervention handler type"),
|
||||||
|
label_suffix="",
|
||||||
|
help_text=_("What type of handler is responsible for the intervention?"),
|
||||||
|
required=False,
|
||||||
|
queryset=KonovaCode.objects.filter(
|
||||||
|
is_archived=False,
|
||||||
|
is_leaf=True,
|
||||||
|
code_lists__in=[CODELIST_HANDLER_ID],
|
||||||
|
),
|
||||||
|
widget=autocomplete.ModelSelect2(
|
||||||
|
url="codes-handler-autocomplete",
|
||||||
|
attrs={
|
||||||
|
"data-placeholder": _("Click for selection"),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
handler_detail = forms.CharField(
|
||||||
|
label=_("Intervention handler detail"),
|
||||||
label_suffix="",
|
label_suffix="",
|
||||||
max_length=255,
|
max_length=255,
|
||||||
required=False,
|
required=False,
|
||||||
help_text=_("Who performs the intervention"),
|
help_text=_("Detail input on the handler"),
|
||||||
widget=forms.TextInput(
|
widget=forms.TextInput(
|
||||||
attrs={
|
attrs={
|
||||||
"placeholder": _("Company Mustermann"),
|
"placeholder": _("Company Mustermann"),
|
||||||
@@ -151,6 +168,7 @@ class NewInterventionForm(BaseForm):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
registration_date = forms.DateField(
|
registration_date = forms.DateField(
|
||||||
label=_("Registration date"),
|
label=_("Registration date"),
|
||||||
label_suffix=_(""),
|
label_suffix=_(""),
|
||||||
@@ -205,7 +223,8 @@ class NewInterventionForm(BaseForm):
|
|||||||
title = self.cleaned_data.get("title", None)
|
title = self.cleaned_data.get("title", None)
|
||||||
_type = self.cleaned_data.get("type", None)
|
_type = self.cleaned_data.get("type", None)
|
||||||
laws = self.cleaned_data.get("laws", None)
|
laws = self.cleaned_data.get("laws", None)
|
||||||
handler = self.cleaned_data.get("handler", None)
|
handler_type = self.cleaned_data.get("handler_type", None)
|
||||||
|
handler_detail = self.cleaned_data.get("handler_detail", None)
|
||||||
registration_office = self.cleaned_data.get("registration_office", None)
|
registration_office = self.cleaned_data.get("registration_office", None)
|
||||||
conservation_office = self.cleaned_data.get("conservation_office", None)
|
conservation_office = self.cleaned_data.get("conservation_office", None)
|
||||||
conservation_file_number = self.cleaned_data.get("conservation_file_number", None)
|
conservation_file_number = self.cleaned_data.get("conservation_file_number", None)
|
||||||
@@ -226,6 +245,10 @@ class NewInterventionForm(BaseForm):
|
|||||||
# Then add the M2M laws to the object
|
# Then add the M2M laws to the object
|
||||||
legal_data.laws.set(laws)
|
legal_data.laws.set(laws)
|
||||||
|
|
||||||
|
handler = Handler.objects.create(
|
||||||
|
type=handler_type,
|
||||||
|
detail=handler_detail
|
||||||
|
)
|
||||||
# Create responsible data object
|
# Create responsible data object
|
||||||
responsibility_data = Responsibility.objects.create(
|
responsibility_data = Responsibility.objects.create(
|
||||||
registration_office=registration_office,
|
registration_office=registration_office,
|
||||||
@@ -284,7 +307,8 @@ class EditInterventionForm(NewInterventionForm):
|
|||||||
"title": self.instance.title,
|
"title": self.instance.title,
|
||||||
"type": self.instance.legal.process_type,
|
"type": self.instance.legal.process_type,
|
||||||
"laws": list(self.instance.legal.laws.values_list("id", flat=True)),
|
"laws": list(self.instance.legal.laws.values_list("id", flat=True)),
|
||||||
"handler": self.instance.responsible.handler,
|
"handler_type": self.instance.responsible.handler.type,
|
||||||
|
"handler_detail": self.instance.responsible.handler.detail,
|
||||||
"registration_office": self.instance.responsible.registration_office,
|
"registration_office": self.instance.responsible.registration_office,
|
||||||
"registration_file_number": self.instance.responsible.registration_file_number,
|
"registration_file_number": self.instance.responsible.registration_file_number,
|
||||||
"conservation_office": self.instance.responsible.conservation_office,
|
"conservation_office": self.instance.responsible.conservation_office,
|
||||||
@@ -313,7 +337,8 @@ class EditInterventionForm(NewInterventionForm):
|
|||||||
title = self.cleaned_data.get("title", None)
|
title = self.cleaned_data.get("title", None)
|
||||||
process_type = self.cleaned_data.get("type", None)
|
process_type = self.cleaned_data.get("type", None)
|
||||||
laws = self.cleaned_data.get("laws", None)
|
laws = self.cleaned_data.get("laws", None)
|
||||||
handler = self.cleaned_data.get("handler", None)
|
handler_type = self.cleaned_data.get("handler_type", None)
|
||||||
|
handler_detail = self.cleaned_data.get("handler_detail", None)
|
||||||
registration_office = self.cleaned_data.get("registration_office", None)
|
registration_office = self.cleaned_data.get("registration_office", None)
|
||||||
registration_file_number = self.cleaned_data.get("registration_file_number", None)
|
registration_file_number = self.cleaned_data.get("registration_file_number", None)
|
||||||
conservation_office = self.cleaned_data.get("conservation_office", None)
|
conservation_office = self.cleaned_data.get("conservation_office", None)
|
||||||
@@ -328,7 +353,10 @@ class EditInterventionForm(NewInterventionForm):
|
|||||||
self.instance.legal.laws.set(laws)
|
self.instance.legal.laws.set(laws)
|
||||||
self.instance.legal.save()
|
self.instance.legal.save()
|
||||||
|
|
||||||
self.instance.responsible.handler = handler
|
self.instance.responsible.handler.type = handler_type
|
||||||
|
self.instance.responsible.handler.detail = handler_detail
|
||||||
|
self.instance.responsible.handler.save()
|
||||||
|
|
||||||
self.instance.responsible.registration_office = registration_office
|
self.instance.responsible.registration_office = registration_office
|
||||||
self.instance.responsible.registration_file_number = registration_file_number
|
self.instance.responsible.registration_file_number = registration_file_number
|
||||||
self.instance.responsible.conservation_office = conservation_office
|
self.instance.responsible.conservation_office = conservation_office
|
||||||
|
|||||||
@@ -427,13 +427,22 @@ class NewDeductionModalForm(BaseModalForm):
|
|||||||
"""
|
"""
|
||||||
super_result = super().is_valid()
|
super_result = super().is_valid()
|
||||||
acc = self.cleaned_data["account"]
|
acc = self.cleaned_data["account"]
|
||||||
|
intervention = self.cleaned_data["intervention"]
|
||||||
|
objects_valid = True
|
||||||
|
|
||||||
if not acc.recorded:
|
if not acc.recorded:
|
||||||
self.add_error(
|
self.add_error(
|
||||||
"account",
|
"account",
|
||||||
_("Eco-account {} is not recorded yet. You can only deduct from recorded accounts.").format(acc.identifier)
|
_("Eco-account {} is not recorded yet. You can only deduct from recorded accounts.").format(acc.identifier)
|
||||||
)
|
)
|
||||||
return False
|
objects_valid = False
|
||||||
|
|
||||||
|
if intervention.is_recorded:
|
||||||
|
self.add_error(
|
||||||
|
"intervention",
|
||||||
|
_("Intervention {} is currently recorded. To change any data on it, the entry must be unrecorded.").format(intervention.identifier)
|
||||||
|
)
|
||||||
|
objects_valid = False
|
||||||
|
|
||||||
rest_surface = self._get_available_surface(acc)
|
rest_surface = self._get_available_surface(acc)
|
||||||
form_surface = float(self.cleaned_data["surface"])
|
form_surface = float(self.cleaned_data["surface"])
|
||||||
@@ -447,7 +456,7 @@ class NewDeductionModalForm(BaseModalForm):
|
|||||||
format_german_float(rest_surface),
|
format_german_float(rest_surface),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
return is_valid_surface and super_result
|
return is_valid_surface and objects_valid and super_result
|
||||||
|
|
||||||
def __create_deduction(self):
|
def __create_deduction(self):
|
||||||
""" Creates the deduction
|
""" Creates the deduction
|
||||||
|
|||||||
64
intervention/migrations/0004_auto_20220303_0956.py
Normal file
64
intervention/migrations/0004_auto_20220303_0956.py
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
# Generated by Django 3.1.3 on 2022-03-03 08:56
|
||||||
|
from django.db import migrations, models, transaction
|
||||||
|
import django.db.models.deletion
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
def migrate_handler(apps, schema_editor):
|
||||||
|
KonovaCode = apps.get_model('codelist', 'KonovaCode')
|
||||||
|
|
||||||
|
Responsibility = apps.get_model('intervention', 'Responsibility')
|
||||||
|
Handler = apps.get_model('intervention', 'Handler')
|
||||||
|
all_responsibs = Responsibility.objects.all()
|
||||||
|
|
||||||
|
if all_responsibs.exists():
|
||||||
|
handler_tmp_code = KonovaCode.objects.get(
|
||||||
|
atom_id=710185,
|
||||||
|
)
|
||||||
|
|
||||||
|
with transaction.atomic():
|
||||||
|
for resp in all_responsibs:
|
||||||
|
handler_old = resp.handler_old
|
||||||
|
handler = Handler.objects.create(
|
||||||
|
type=handler_tmp_code,
|
||||||
|
detail=handler_old
|
||||||
|
)
|
||||||
|
resp.handler = handler
|
||||||
|
resp.save()
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('codelist', '0001_initial'),
|
||||||
|
('intervention', '0003_intervention_teams'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Handler',
|
||||||
|
fields=[
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||||
|
('detail', models.CharField(blank=True, max_length=500, null=True)),
|
||||||
|
('type', models.ForeignKey(blank=True, limit_choices_to={'code_lists__in': [1052], 'is_archived': False, 'is_selectable': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, to='codelist.konovacode')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='responsibility',
|
||||||
|
old_name='handler',
|
||||||
|
new_name='handler_old',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='responsibility',
|
||||||
|
name='handler',
|
||||||
|
field=models.ForeignKey(blank=True, help_text="Refers to 'Eingriffsverursacher' or 'Maßnahmenträger'", null=True, on_delete=django.db.models.deletion.SET_NULL, to='intervention.handler'),
|
||||||
|
),
|
||||||
|
migrations.RunPython(migrate_handler),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='responsibility',
|
||||||
|
name='handler_old'
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -13,6 +13,7 @@ from django.db.models.fields.files import FieldFile
|
|||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
|
from intervention.tasks import celery_export_to_egon
|
||||||
from user.models import User
|
from user.models import User
|
||||||
from django.db import models, transaction
|
from django.db import models, transaction
|
||||||
from django.db.models import QuerySet
|
from django.db.models import QuerySet
|
||||||
@@ -131,6 +132,16 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec
|
|||||||
self.add_log_entry_to_compensations(log_entry)
|
self.add_log_entry_to_compensations(log_entry)
|
||||||
return log_entry
|
return log_entry
|
||||||
|
|
||||||
|
def send_data_to_egon(self):
|
||||||
|
""" Performs the export to rabbitmq of this intervention's data
|
||||||
|
|
||||||
|
FOLLOWING BACKWARDS COMPATIBILITY LOGIC
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
celery_export_to_egon.delay(self.id)
|
||||||
|
|
||||||
def set_recorded(self, user: User) -> UserActionLogEntry:
|
def set_recorded(self, user: User) -> UserActionLogEntry:
|
||||||
log_entry = super().set_recorded(user)
|
log_entry = super().set_recorded(user)
|
||||||
self.add_log_entry_to_compensations(log_entry)
|
self.add_log_entry_to_compensations(log_entry)
|
||||||
@@ -171,6 +182,8 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec
|
|||||||
intervention=self,
|
intervention=self,
|
||||||
)
|
)
|
||||||
self.mark_as_edited(user, form.request, edit_comment=PAYMENT_ADDED)
|
self.mark_as_edited(user, form.request, edit_comment=PAYMENT_ADDED)
|
||||||
|
|
||||||
|
self.send_data_to_egon()
|
||||||
return pay
|
return pay
|
||||||
|
|
||||||
def add_revocation(self, form):
|
def add_revocation(self, form):
|
||||||
@@ -335,6 +348,7 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec
|
|||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
payment.delete()
|
payment.delete()
|
||||||
self.mark_as_edited(user, request=form.request, edit_comment=PAYMENT_REMOVED)
|
self.mark_as_edited(user, request=form.request, edit_comment=PAYMENT_REMOVED)
|
||||||
|
self.send_data_to_egon()
|
||||||
|
|
||||||
|
|
||||||
class InterventionDocument(AbstractDocument):
|
class InterventionDocument(AbstractDocument):
|
||||||
|
|||||||
@@ -6,10 +6,42 @@ Created on: 15.11.21
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from codelist.models import KonovaCode
|
from codelist.models import KonovaCode
|
||||||
from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID, CODELIST_REGISTRATION_OFFICE_ID
|
from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID, CODELIST_REGISTRATION_OFFICE_ID, \
|
||||||
|
CODELIST_HANDLER_ID
|
||||||
from konova.models import UuidModel
|
from konova.models import UuidModel
|
||||||
|
from konova.utils.message_templates import UNKNOWN, NO_DETAILS
|
||||||
|
|
||||||
|
|
||||||
|
class Handler(UuidModel):
|
||||||
|
""" The handler of an entry
|
||||||
|
|
||||||
|
Refers to 'Eingriffsverursacher' or 'Maßnahmenträger'
|
||||||
|
|
||||||
|
"""
|
||||||
|
type = models.ForeignKey(
|
||||||
|
KonovaCode,
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
limit_choices_to={
|
||||||
|
"code_lists__in": [CODELIST_HANDLER_ID],
|
||||||
|
"is_selectable": True,
|
||||||
|
"is_archived": False,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
detail = models.CharField(
|
||||||
|
max_length=500,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
detail = self.detail or NO_DETAILS
|
||||||
|
_type = self.type.long_name if self.type is not None else UNKNOWN
|
||||||
|
return f'{_type}, {detail}'
|
||||||
|
|
||||||
|
|
||||||
class Responsibility(UuidModel):
|
class Responsibility(UuidModel):
|
||||||
@@ -43,11 +75,17 @@ class Responsibility(UuidModel):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
conservation_file_number = models.CharField(max_length=1000, blank=True, null=True)
|
conservation_file_number = models.CharField(max_length=1000, blank=True, null=True)
|
||||||
handler = models.CharField(max_length=500, null=True, blank=True, help_text="Refers to 'Eingriffsverursacher' or 'Maßnahmenträger'")
|
handler = models.ForeignKey(
|
||||||
|
Handler,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
help_text="Refers to 'Eingriffsverursacher' or 'Maßnahmenträger'",
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "ZB: {} | ETS: {} | Handler: {}".format(
|
return "ZB: {} | ETS: {} | Handler: {}".format(
|
||||||
self.registration_office,
|
self.registration_office,
|
||||||
self.conservation_office,
|
self.conservation_office,
|
||||||
self.handler
|
str(self.handler)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,4 +6,11 @@ Created on: 30.11.20
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
INTERVENTION_IDENTIFIER_LENGTH = 6
|
INTERVENTION_IDENTIFIER_LENGTH = 6
|
||||||
INTERVENTION_IDENTIFIER_TEMPLATE = "EIV-{}"
|
INTERVENTION_IDENTIFIER_TEMPLATE = "EIV-{}"
|
||||||
|
|
||||||
|
# EGON connection settings via rabbitmq
|
||||||
|
# NEEDED FOR BACKWARDS COMPATIBILITY
|
||||||
|
EGON_RABBITMQ_HOST = "CHANGE_ME"
|
||||||
|
EGON_RABBITMQ_PORT = "CHANGE_ME"
|
||||||
|
EGON_RABBITMQ_USER = "CHANGE_ME"
|
||||||
|
EGON_RABBITMQ_PW = "CHANGE_ME"
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ class InterventionTable(BaseTable, TableRenderMixin):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
parcels = value.get_underlying_parcels().values_list(
|
parcels = value.get_underlying_parcels().values_list(
|
||||||
"gmrkng",
|
"parcel_group__name",
|
||||||
flat=True
|
flat=True
|
||||||
).distinct()
|
).distinct()
|
||||||
html = render_to_string(
|
html = render_to_string(
|
||||||
@@ -177,9 +177,7 @@ class InterventionTable(BaseTable, TableRenderMixin):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
html = ""
|
html = ""
|
||||||
has_access = value.filter(
|
has_access = record.is_shared_with(self.user)
|
||||||
id=self.user.id
|
|
||||||
).exists()
|
|
||||||
|
|
||||||
html += self.render_icn(
|
html += self.render_icn(
|
||||||
tooltip=_("Full access granted") if has_access else _("Access not granted"),
|
tooltip=_("Full access granted") if has_access else _("Access not granted"),
|
||||||
|
|||||||
18
intervention/tasks.py
Normal file
18
intervention/tasks.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||||
|
Created on: 21.03.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from celery import shared_task
|
||||||
|
|
||||||
|
from intervention.utils.egon_export import EgonExporter
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task
|
||||||
|
def celery_export_to_egon(intervention_id: str):
|
||||||
|
from intervention.models import Intervention
|
||||||
|
intervention = Intervention.objects.get(id=intervention_id)
|
||||||
|
egon_exporter = EgonExporter(intervention)
|
||||||
|
egon_exporter.export_to_rabbitmq()
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n l10n static fontawesome_5 humanize %}
|
{% load i18n l10n static fontawesome_5 %}
|
||||||
|
|
||||||
{% block head %}
|
{% block head %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
@@ -106,9 +106,15 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans 'Last modified' %}</th>
|
<th scope="row">{% trans 'Last modified' %}</th>
|
||||||
<td class="align-middle">
|
<td class="align-middle">
|
||||||
{{obj.created.timestamp|default_if_none:""|naturalday}}
|
{% if obj.modified %}
|
||||||
<br>
|
{{obj.modified.timestamp|default_if_none:""}}
|
||||||
{{obj.created.user.username}}
|
<br>
|
||||||
|
{{obj.modified.user.username}}
|
||||||
|
{% else %}
|
||||||
|
{{obj.created.timestamp|default_if_none:""}}
|
||||||
|
<br>
|
||||||
|
{{obj.created.user.username}}
|
||||||
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -131,7 +137,7 @@
|
|||||||
{% include 'map/geom_form.html' %}
|
{% include 'map/geom_form.html' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% include 'konova/includes/parcels.html' %}
|
{% include 'konova/includes/parcels/parcels.html' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% include 'konova/includes/comment_card.html' %}
|
{% include 'konova/includes/comment_card.html' %}
|
||||||
|
|||||||
@@ -97,17 +97,10 @@
|
|||||||
{% include 'map/geom_form.html' %}
|
{% include 'map/geom_form.html' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% include 'konova/includes/parcels.html' %}
|
{% include 'konova/includes/parcels/parcels.html' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-6 col-md-6 col-lg-6">
|
{% include 'konova/includes/report/qrcodes.html' %}
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -89,6 +89,30 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase):
|
|||||||
self.assertIn(self.superuser, obj.users.all())
|
self.assertIn(self.superuser, obj.users.all())
|
||||||
self.assertEqual(1, obj.users.count())
|
self.assertEqual(1, obj.users.count())
|
||||||
|
|
||||||
|
def test_non_editable_after_recording(self):
|
||||||
|
""" Tests that the intervention can not be edited after being recorded
|
||||||
|
|
||||||
|
User must be redirected to another page
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.assertIsNotNone(self.intervention)
|
||||||
|
self.assertFalse(self.intervention.is_recorded)
|
||||||
|
edit_url = reverse("intervention:edit", args=(self.intervention.id,))
|
||||||
|
response = self.client_user.get(edit_url)
|
||||||
|
has_redirect = response.status_code == 302
|
||||||
|
self.assertFalse(has_redirect)
|
||||||
|
|
||||||
|
self.intervention.set_recorded(self.user)
|
||||||
|
self.assertTrue(self.intervention.is_recorded)
|
||||||
|
|
||||||
|
edit_url = reverse("intervention:edit", args=(self.intervention.id,))
|
||||||
|
response = self.client_user.get(edit_url)
|
||||||
|
has_redirect = response.status_code == 302
|
||||||
|
self.assertTrue(has_redirect)
|
||||||
|
self.intervention.set_unrecorded(self.user)
|
||||||
|
|
||||||
def test_checkability(self):
|
def test_checkability(self):
|
||||||
""" Tests that the intervention can only be checked if all required data has been added
|
""" Tests that the intervention can only be checked if all required data has been added
|
||||||
|
|
||||||
|
|||||||
250
intervention/utils/egon_export.py
Normal file
250
intervention/utils/egon_export.py
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||||
|
Created on: 07.03.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
import base64
|
||||||
|
import json
|
||||||
|
|
||||||
|
import pika
|
||||||
|
import xmltodict
|
||||||
|
from django.db.models import Sum
|
||||||
|
|
||||||
|
from intervention.settings import EGON_RABBITMQ_HOST, EGON_RABBITMQ_USER, EGON_RABBITMQ_PW, EGON_RABBITMQ_PORT
|
||||||
|
from konova.sub_settings.django_settings import DEFAULT_DATE_FORMAT
|
||||||
|
|
||||||
|
from konova.sub_settings.lanis_settings import DEFAULT_SRID_RLP
|
||||||
|
|
||||||
|
|
||||||
|
class EgonExporter:
|
||||||
|
"""
|
||||||
|
EGON is the payment management system of SNU RLP. Due to compatibility reasons we need to provide the old style
|
||||||
|
of data transmission between KSP and EGON:
|
||||||
|
1. Create GML from intervention object
|
||||||
|
2. Send created GML to the appropriate RabbitMQ channel
|
||||||
|
"""
|
||||||
|
intervention = None
|
||||||
|
gml_builder = None
|
||||||
|
|
||||||
|
def __init__(self, intervention):
|
||||||
|
self.intervention = intervention
|
||||||
|
self.gml_builder = EgonGmlBuilder(intervention)
|
||||||
|
|
||||||
|
def export_to_rabbitmq(self):
|
||||||
|
""" Sends the exporter gml to message broker rabbitmq to be fetched by EGON application from there
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
msg = {
|
||||||
|
"nachricht": self.gml_builder.gml,
|
||||||
|
}
|
||||||
|
msg = json.dumps(msg)
|
||||||
|
print(msg)
|
||||||
|
credentials = pika.PlainCredentials(EGON_RABBITMQ_USER, EGON_RABBITMQ_PW)
|
||||||
|
params = pika.ConnectionParameters(
|
||||||
|
EGON_RABBITMQ_HOST,
|
||||||
|
EGON_RABBITMQ_PORT,
|
||||||
|
"/",
|
||||||
|
credentials
|
||||||
|
)
|
||||||
|
conn = pika.BlockingConnection(params)
|
||||||
|
channel = conn.channel()
|
||||||
|
channel.basic_publish(
|
||||||
|
exchange="",
|
||||||
|
routing_key="KSP_EGON",
|
||||||
|
body=msg.encode("utf-8"),
|
||||||
|
)
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
|
class EgonGmlBuilder:
|
||||||
|
"""
|
||||||
|
Creates the GML for EGON export
|
||||||
|
"""
|
||||||
|
intervention = None
|
||||||
|
gml = None
|
||||||
|
|
||||||
|
def __init__(self, intervention):
|
||||||
|
self.intervention = intervention
|
||||||
|
self.gml = self.build_gml()
|
||||||
|
|
||||||
|
def _gen_flurstuecksKennzeichen(self, parcel):
|
||||||
|
""" Generates oneo:flurstuecksKennzeichen to provide backwards compatibility
|
||||||
|
|
||||||
|
Args:
|
||||||
|
parcel (Parcel): The requested parcel
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str
|
||||||
|
"""
|
||||||
|
gmrkng_code = "{0:06d}".format(int(parcel.parcel_group.key) or 0)
|
||||||
|
flr_code = "{0:03d}".format(int(parcel.flr or 0))
|
||||||
|
flrstckzhlr_code = "{0:05d}".format(int(parcel.flrstck_zhlr or 0))
|
||||||
|
flrstcknnr_code = "{0:06d}".format(int(parcel.flrstck_nnr or 0))
|
||||||
|
return gmrkng_code + flr_code + flrstckzhlr_code + flrstcknnr_code
|
||||||
|
|
||||||
|
def _sum_all_payments(self):
|
||||||
|
all_payments = self.intervention.payments.aggregate(
|
||||||
|
summed=Sum("amount")
|
||||||
|
)["summed"]
|
||||||
|
return all_payments
|
||||||
|
|
||||||
|
def _gen_kompensationsArt(self) -> (str, int):
|
||||||
|
comp_type = "Ersatzzahlung"
|
||||||
|
comp_type_code = 774898901
|
||||||
|
if self.intervention.compensations.exists():
|
||||||
|
comp_type += " und Kompensation"
|
||||||
|
comp_type_code = 771655351
|
||||||
|
return comp_type, comp_type_code
|
||||||
|
|
||||||
|
def _gen_geometry_list(self):
|
||||||
|
geom = self.intervention.geometry.geom
|
||||||
|
geom.transform(DEFAULT_SRID_RLP)
|
||||||
|
geoms_list = [
|
||||||
|
{
|
||||||
|
"gml:Polygon": {
|
||||||
|
"gml:exterior": {
|
||||||
|
"gml:LinearRing": {
|
||||||
|
"gml:posList": " ".join([f"{str(coord[0])} {str(coord[1])}" for coord in coords[0]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} for coords in geom.coords
|
||||||
|
]
|
||||||
|
return geoms_list
|
||||||
|
|
||||||
|
def _gen_raumreferenz(self):
|
||||||
|
parcels = self.intervention.get_underlying_parcels()
|
||||||
|
spatial_reference_list = [
|
||||||
|
{
|
||||||
|
"oneo:datumAbgleich": None,
|
||||||
|
"oneo:ortsangabe": {
|
||||||
|
"oneo:Ortsangaben": {
|
||||||
|
"oneo:kreisSchluessel": {
|
||||||
|
"xlink:href": f"http://register.naturschutz.rlp.de/repository/services/referenzliste/588/{parcel.district.key}",
|
||||||
|
},
|
||||||
|
"oneo:gemeindeSchluessel": {
|
||||||
|
"xlink:href": f"http://register.naturschutz.rlp.de/repository/services/referenzliste/910/{parcel.municipal.key}",
|
||||||
|
},
|
||||||
|
"oneo:verbandsgemeindeSchluessel": {
|
||||||
|
"xlink:href": f"http://register.naturschutz.rlp.de/repository/services/referenzliste/589/{None}",
|
||||||
|
},
|
||||||
|
"oneo:flurstuecksKennzeichen": self._gen_flurstuecksKennzeichen(parcel),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
} for parcel in parcels
|
||||||
|
]
|
||||||
|
return spatial_reference_list
|
||||||
|
|
||||||
|
def _gen_foto(self):
|
||||||
|
revoc_docs, regular_docs = self.intervention.get_documents()
|
||||||
|
docs_list = [
|
||||||
|
{
|
||||||
|
"oneo:Foto": {
|
||||||
|
"oneo:aufnahmezeitpunkt": doc.date_of_creation.strftime(DEFAULT_DATE_FORMAT),
|
||||||
|
"oneo:bemerkung": doc.comment,
|
||||||
|
"oneo:fotoverweis": base64.b64encode(doc.file.read()).decode("utf-8"),
|
||||||
|
"oneo:dateiname": doc.title,
|
||||||
|
"oneo:hauptfoto": False,
|
||||||
|
}
|
||||||
|
} for doc in regular_docs
|
||||||
|
]
|
||||||
|
return docs_list
|
||||||
|
|
||||||
|
def build_gml(self):
|
||||||
|
comp_type, comp_type_code = self._gen_kompensationsArt()
|
||||||
|
payment = self.intervention.payments.first()
|
||||||
|
payment_date = None
|
||||||
|
if payment is not None:
|
||||||
|
payment_date = payment.due_on
|
||||||
|
payment_date = payment_date.strftime(DEFAULT_DATE_FORMAT)
|
||||||
|
|
||||||
|
cons_office = self.intervention.responsible.conservation_office
|
||||||
|
reg_office = self.intervention.responsible.registration_office
|
||||||
|
law = self.intervention.legal.laws.first()
|
||||||
|
process_type = self.intervention.legal.process_type
|
||||||
|
handler = self.intervention.responsible.handler
|
||||||
|
reg_date = self.intervention.legal.registration_date
|
||||||
|
bind_date = self.intervention.legal.binding_date
|
||||||
|
|
||||||
|
xml_dict = {
|
||||||
|
"wfs:FeatureCollection": {
|
||||||
|
"@xmlns:wfs": "http://www.opengis.net/wfs",
|
||||||
|
"@xmlns:xlink": "http://www.w3.org/1999/xlink",
|
||||||
|
"@xmlns:oneo": "http://www.osiris-projekt.rlp.de/oneo",
|
||||||
|
"@xmlns:gmlexr": "http://www.opengis.net/gml/3.3/exr",
|
||||||
|
"@xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
|
||||||
|
"@xmlns:gml": "http://www.opengis.net/gml/3.2",
|
||||||
|
"oneo:Eingriffsverfahren": {
|
||||||
|
"@gml:id": self.intervention.identifier,
|
||||||
|
"oneo:azEintragungsstelle": self.intervention.responsible.conservation_file_number,
|
||||||
|
"oneo:azZulassungsstelle": self.intervention.responsible.registration_file_number,
|
||||||
|
"oneo:bemerkungZulassungsstelle": None,
|
||||||
|
"oneo:eintragungsstelle": {
|
||||||
|
"@xlink:href": f"http://register.naturschutz.rlp.de/repository/services/referenzliste/907/{cons_office.atom_id if cons_office else None}",
|
||||||
|
"#text": cons_office.long_name if cons_office else None
|
||||||
|
},
|
||||||
|
"oneo:zulassungsstelle": {
|
||||||
|
"@xlink:href": f"http://register.naturschutz.rlp.de/repository/services/referenzliste/1053/{reg_office.atom_id if reg_office else None}",
|
||||||
|
"#text": reg_office.long_name if reg_office else None
|
||||||
|
},
|
||||||
|
"oneo:ersatzzahlung": self._sum_all_payments(),
|
||||||
|
"oneo:kompensationsart": {
|
||||||
|
"@xlink:href": f"http://register.naturschutz.rlp.de/repository/services/referenzliste/88140/{comp_type_code}",
|
||||||
|
"#text": comp_type
|
||||||
|
},
|
||||||
|
"oneo:verfahrensrecht": {
|
||||||
|
"@xlink:href": f"http://register.naturschutz.rlp.de/repository/services/referenzliste/1048/{law.atom_id if law else None}",
|
||||||
|
"#text": law.short_name if law else None
|
||||||
|
},
|
||||||
|
"oneo:verfahrenstyp": {
|
||||||
|
"@xlink:href": f"http://register.naturschutz.rlp.de/repository/services/referenzliste/44382/{process_type.atom_id if process_type else None}",
|
||||||
|
"#text": process_type.long_name if process_type else None,
|
||||||
|
},
|
||||||
|
"oneo:eingreifer": {
|
||||||
|
"oneo:Eingreifer": {
|
||||||
|
"oneo:art": {
|
||||||
|
"@xlink:href": f"http://register.naturschutz.rlp.de/repository/services/referenzliste/1053/{handler.type.atom_id if handler.type else None}",
|
||||||
|
"#text": handler.type.long_name if handler.type else None,
|
||||||
|
},
|
||||||
|
"oneo:bemerkung": handler.detail if handler else None,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"oneo:erfasser": {
|
||||||
|
"oneo:Erfasser": {
|
||||||
|
"oneo:name": None,
|
||||||
|
"oneo:bemerkung": None,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"oneo:zulassung": {
|
||||||
|
"oneo:Zulassungstermin": {
|
||||||
|
"oneo:bauBeginn": payment_date,
|
||||||
|
"oneo:erlass": reg_date.strftime(DEFAULT_DATE_FORMAT) if reg_date else None,
|
||||||
|
"oneo:rechtsKraft": bind_date.strftime(DEFAULT_DATE_FORMAT) if bind_date else None,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"oneo:geometrie": {
|
||||||
|
"gml:multiSurfaceProperty": {
|
||||||
|
"gml:MultiPolygon": {
|
||||||
|
"@srsName": f"http://www.opengis.net/gml/srs/epsg.xml#{DEFAULT_SRID_RLP}",
|
||||||
|
"gml:polygonMember": self._gen_geometry_list(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"oneo:kennung": self.intervention.identifier,
|
||||||
|
"oneo:bezeichnung": self.intervention.title,
|
||||||
|
"oneo:bemerkung": self.intervention.comment,
|
||||||
|
"oneo:verantwortlicheStelle": None,
|
||||||
|
"oneo:veroffentlichtAm": None,
|
||||||
|
"oneo:raumreferenz": {
|
||||||
|
"oneo:Raumreferenz": self._gen_raumreferenz(),
|
||||||
|
},
|
||||||
|
"oneo:foto": self._gen_foto(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
gml = xmltodict.unparse(xml_dict)
|
||||||
|
return gml
|
||||||
@@ -18,7 +18,8 @@ from konova.utils.documents import remove_document, get_document
|
|||||||
from konova.utils.generators import generate_qr_code
|
from konova.utils.generators import generate_qr_code
|
||||||
from konova.utils.message_templates import INTERVENTION_INVALID, FORM_INVALID, IDENTIFIER_REPLACED, \
|
from konova.utils.message_templates import INTERVENTION_INVALID, FORM_INVALID, IDENTIFIER_REPLACED, \
|
||||||
CHECKED_RECORDED_RESET, DEDUCTION_REMOVED, DEDUCTION_ADDED, REVOCATION_ADDED, REVOCATION_REMOVED, \
|
CHECKED_RECORDED_RESET, DEDUCTION_REMOVED, DEDUCTION_ADDED, REVOCATION_ADDED, REVOCATION_REMOVED, \
|
||||||
COMPENSATION_REMOVED_TEMPLATE, DOCUMENT_ADDED, DEDUCTION_EDITED, REVOCATION_EDITED, DOCUMENT_EDITED
|
COMPENSATION_REMOVED_TEMPLATE, DOCUMENT_ADDED, DEDUCTION_EDITED, REVOCATION_EDITED, DOCUMENT_EDITED, \
|
||||||
|
RECORDED_BLOCKS_EDIT
|
||||||
from konova.utils.user_checks import in_group
|
from konova.utils.user_checks import in_group
|
||||||
|
|
||||||
|
|
||||||
@@ -302,6 +303,13 @@ def edit_view(request: HttpRequest, id: str):
|
|||||||
template = "intervention/form/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)
|
||||||
|
if intervention.is_recorded:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
RECORDED_BLOCKS_EDIT
|
||||||
|
)
|
||||||
|
return redirect("intervention:detail", id=id)
|
||||||
|
|
||||||
# Create forms, initialize with values from db/from POST request
|
# Create forms, initialize with values from db/from POST request
|
||||||
data_form = EditInterventionForm(request.POST or None, instance=intervention)
|
data_form = EditInterventionForm(request.POST or None, instance=intervention)
|
||||||
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=intervention)
|
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=intervention)
|
||||||
@@ -693,19 +701,22 @@ def report_view(request:HttpRequest, id: str):
|
|||||||
distinct_deductions = intervention.deductions.all().distinct(
|
distinct_deductions = intervention.deductions.all().distinct(
|
||||||
"account"
|
"account"
|
||||||
)
|
)
|
||||||
qrcode_img = generate_qr_code(
|
qrcode_url = request.build_absolute_uri(reverse("intervention:report", args=(id,)))
|
||||||
request.build_absolute_uri(reverse("intervention:report", args=(id,))),
|
qrcode_img = generate_qr_code(qrcode_url, 10)
|
||||||
10
|
qrcode_lanis_url = intervention.get_LANIS_link()
|
||||||
)
|
qrcode_img_lanis = generate_qr_code(qrcode_lanis_url, 7)
|
||||||
qrcode_img_lanis = generate_qr_code(
|
|
||||||
intervention.get_LANIS_link(),
|
|
||||||
7
|
|
||||||
)
|
|
||||||
context = {
|
context = {
|
||||||
"obj": intervention,
|
"obj": intervention,
|
||||||
"deductions": distinct_deductions,
|
"deductions": distinct_deductions,
|
||||||
"qrcode": qrcode_img,
|
"qrcode": {
|
||||||
"qrcode_lanis": qrcode_img_lanis,
|
"img": qrcode_img,
|
||||||
|
"url": qrcode_url,
|
||||||
|
},
|
||||||
|
"qrcode_lanis": {
|
||||||
|
"img": qrcode_img_lanis,
|
||||||
|
"url": qrcode_lanis_url,
|
||||||
|
},
|
||||||
"geom_form": geom_form,
|
"geom_form": geom_form,
|
||||||
"parcels": parcels,
|
"parcels": parcels,
|
||||||
TAB_TITLE_IDENTIFIER: tab_title,
|
TAB_TITLE_IDENTIFIER: tab_title,
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ Created on: 22.07.21
|
|||||||
"""
|
"""
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from konova.models import Geometry, Deadline, GeometryConflict, Parcel, District
|
from konova.models import Geometry, Deadline, GeometryConflict, Parcel, District, Municipal, ParcelGroup
|
||||||
|
from konova.sub_settings.lanis_settings import DEFAULT_SRID_RLP
|
||||||
from konova.utils.message_templates import COMPENSATION_REMOVED_TEMPLATE
|
from konova.utils.message_templates import COMPENSATION_REMOVED_TEMPLATE
|
||||||
from user.models import UserAction
|
from user.models import UserAction
|
||||||
|
|
||||||
@@ -16,13 +17,28 @@ class GeometryAdmin(admin.ModelAdmin):
|
|||||||
list_display = [
|
list_display = [
|
||||||
"id",
|
"id",
|
||||||
"created",
|
"created",
|
||||||
|
"st_area",
|
||||||
]
|
]
|
||||||
|
readonly_fields = [
|
||||||
|
"st_area",
|
||||||
|
"created",
|
||||||
|
"modified",
|
||||||
|
]
|
||||||
|
|
||||||
|
def st_area(self, obj):
|
||||||
|
val = None
|
||||||
|
geom = obj.geom
|
||||||
|
if geom is not None:
|
||||||
|
geom.transform(ct=DEFAULT_SRID_RLP)
|
||||||
|
val = geom.area
|
||||||
|
return val
|
||||||
|
st_area.short_description = f"Area (srid={DEFAULT_SRID_RLP})"
|
||||||
|
|
||||||
|
|
||||||
class ParcelAdmin(admin.ModelAdmin):
|
class ParcelAdmin(admin.ModelAdmin):
|
||||||
list_display = [
|
list_display = [
|
||||||
"id",
|
"id",
|
||||||
"gmrkng",
|
"parcel_group",
|
||||||
"flr",
|
"flr",
|
||||||
"flrstck_nnr",
|
"flrstck_nnr",
|
||||||
"flrstck_zhlr",
|
"flrstck_zhlr",
|
||||||
@@ -32,9 +48,27 @@ class ParcelAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
class DistrictAdmin(admin.ModelAdmin):
|
class DistrictAdmin(admin.ModelAdmin):
|
||||||
list_display = [
|
list_display = [
|
||||||
|
"name",
|
||||||
|
"key",
|
||||||
|
"id",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class MunicipalAdmin(admin.ModelAdmin):
|
||||||
|
list_display = [
|
||||||
|
"name",
|
||||||
|
"key",
|
||||||
|
"district",
|
||||||
|
"id",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ParcelGroupAdmin(admin.ModelAdmin):
|
||||||
|
list_display = [
|
||||||
|
"name",
|
||||||
|
"key",
|
||||||
|
"municipal",
|
||||||
"id",
|
"id",
|
||||||
"gmnd",
|
|
||||||
"krs",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@@ -105,5 +139,7 @@ class BaseObjectAdmin(BaseResourceAdmin):
|
|||||||
#admin.site.register(Geometry, GeometryAdmin)
|
#admin.site.register(Geometry, GeometryAdmin)
|
||||||
#admin.site.register(Parcel, ParcelAdmin)
|
#admin.site.register(Parcel, ParcelAdmin)
|
||||||
#admin.site.register(District, DistrictAdmin)
|
#admin.site.register(District, DistrictAdmin)
|
||||||
|
#admin.site.register(Municipal, MunicipalAdmin)
|
||||||
|
#admin.site.register(ParcelGroup, ParcelGroupAdmin)
|
||||||
#admin.site.register(GeometryConflict, GeometryConflictAdmin)
|
#admin.site.register(GeometryConflict, GeometryConflictAdmin)
|
||||||
#admin.site.register(Deadline, DeadlineAdmin)
|
#admin.site.register(Deadline, DeadlineAdmin)
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ from django.db.models import Q
|
|||||||
from codelist.models import KonovaCode
|
from codelist.models import KonovaCode
|
||||||
from codelist.settings import CODELIST_COMPENSATION_ACTION_ID, CODELIST_BIOTOPES_ID, CODELIST_LAW_ID, \
|
from codelist.settings import CODELIST_COMPENSATION_ACTION_ID, CODELIST_BIOTOPES_ID, CODELIST_LAW_ID, \
|
||||||
CODELIST_REGISTRATION_OFFICE_ID, CODELIST_CONSERVATION_OFFICE_ID, CODELIST_PROCESS_TYPE_ID, \
|
CODELIST_REGISTRATION_OFFICE_ID, CODELIST_CONSERVATION_OFFICE_ID, CODELIST_PROCESS_TYPE_ID, \
|
||||||
CODELIST_BIOTOPES_EXTRA_CODES_ID, CODELIST_COMPENSATION_ACTION_DETAIL_ID
|
CODELIST_BIOTOPES_EXTRA_CODES_ID, CODELIST_COMPENSATION_ACTION_DETAIL_ID, CODELIST_HANDLER_ID
|
||||||
from compensation.models import EcoAccount
|
from compensation.models import EcoAccount
|
||||||
from intervention.models import Intervention
|
from intervention.models import Intervention
|
||||||
|
|
||||||
@@ -52,14 +52,16 @@ class InterventionAutocomplete(Select2QuerySetView):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
if self.request.user.is_anonymous:
|
user = self.request.user
|
||||||
|
if user.is_anonymous:
|
||||||
return Intervention.objects.none()
|
return Intervention.objects.none()
|
||||||
qs = Intervention.objects.filter(
|
qs = Intervention.objects.filter(
|
||||||
deleted=None,
|
Q(deleted=None) &
|
||||||
users__in=[self.request.user],
|
Q(users__in=[user]) |
|
||||||
|
Q(teams__in=user.teams.all())
|
||||||
).order_by(
|
).order_by(
|
||||||
"identifier"
|
"identifier"
|
||||||
)
|
).distinct()
|
||||||
if self.q:
|
if self.q:
|
||||||
qs = qs.filter(
|
qs = qs.filter(
|
||||||
Q(identifier__icontains=self.q) |
|
Q(identifier__icontains=self.q) |
|
||||||
@@ -357,3 +359,18 @@ class ConservationOfficeCodeAutocomplete(KonovaCodeAutocomplete):
|
|||||||
|
|
||||||
def get_result_label(self, result):
|
def get_result_label(self, result):
|
||||||
return f"{result.long_name} ({result.short_name})"
|
return f"{result.long_name} ({result.short_name})"
|
||||||
|
|
||||||
|
|
||||||
|
class HandlerCodeAutocomplete(KonovaCodeAutocomplete):
|
||||||
|
"""
|
||||||
|
Due to limitations of the django dal package, we need to subclass for each code list
|
||||||
|
"""
|
||||||
|
group_by_related = "parent"
|
||||||
|
related_field_name = "long_name"
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.c = CODELIST_HANDLER_ID
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_result_label(self, result):
|
||||||
|
return result.long_name
|
||||||
|
|||||||
@@ -145,26 +145,20 @@ class GeoReferencedTableFilterMixin(django_filters.FilterSet):
|
|||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
def _filter_parcel_reference(self, queryset, name, value, filter_value) -> QuerySet:
|
def _filter_parcel_reference(self, queryset, filter_q) -> QuerySet:
|
||||||
""" Filters the parcel entries by a given filter_value.
|
""" Filters the parcel entries by a given filter_q
|
||||||
|
|
||||||
filter_value may already include further filter annotations like 'xy__icontains'
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
queryset ():
|
queryset (QuerySet): The queryset
|
||||||
name ():
|
filter_q (Q): The Q-style filter expression
|
||||||
value ():
|
|
||||||
filter_value ():
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
_filter = {
|
|
||||||
filter_value: value
|
|
||||||
}
|
|
||||||
matching_parcels = Parcel.objects.filter(
|
matching_parcels = Parcel.objects.filter(
|
||||||
**_filter
|
filter_q
|
||||||
)
|
)
|
||||||
|
|
||||||
related_geoms = matching_parcels.values(
|
related_geoms = matching_parcels.values(
|
||||||
"geometries"
|
"geometries"
|
||||||
).distinct()
|
).distinct()
|
||||||
@@ -185,8 +179,9 @@ class GeoReferencedTableFilterMixin(django_filters.FilterSet):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
matching_districts = District.objects.filter(
|
matching_districts = District.objects.filter(
|
||||||
krs__icontains=value
|
Q(name__icontains=value) |
|
||||||
)
|
Q(key__icontains=value)
|
||||||
|
).distinct()
|
||||||
matching_parcels = Parcel.objects.filter(
|
matching_parcels = Parcel.objects.filter(
|
||||||
district__in=matching_districts
|
district__in=matching_districts
|
||||||
)
|
)
|
||||||
@@ -209,7 +204,10 @@ class GeoReferencedTableFilterMixin(django_filters.FilterSet):
|
|||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
queryset = self._filter_parcel_reference(queryset, name, value, "gmrkng__icontains")
|
queryset = self._filter_parcel_reference(
|
||||||
|
queryset,
|
||||||
|
Q(parcel_group__name__icontains=value) | Q(parcel_group__key__icontains=value),
|
||||||
|
)
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
def filter_parcel(self, queryset, name, value) -> QuerySet:
|
def filter_parcel(self, queryset, name, value) -> QuerySet:
|
||||||
@@ -224,7 +222,10 @@ class GeoReferencedTableFilterMixin(django_filters.FilterSet):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
value = value.replace("-", "")
|
value = value.replace("-", "")
|
||||||
queryset = self._filter_parcel_reference(queryset, name, value, "flr")
|
queryset = self._filter_parcel_reference(
|
||||||
|
queryset,
|
||||||
|
Q(flr=value),
|
||||||
|
)
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
def filter_parcel_counter(self, queryset, name, value) -> QuerySet:
|
def filter_parcel_counter(self, queryset, name, value) -> QuerySet:
|
||||||
@@ -239,7 +240,10 @@ class GeoReferencedTableFilterMixin(django_filters.FilterSet):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
value = value.replace("-", "")
|
value = value.replace("-", "")
|
||||||
queryset = self._filter_parcel_reference(queryset, name, value, "flrstck_zhlr")
|
queryset = self._filter_parcel_reference(
|
||||||
|
queryset,
|
||||||
|
Q(flrstck_zhlr=value)
|
||||||
|
)
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
def filter_parcel_number(self, queryset, name, value) -> QuerySet:
|
def filter_parcel_number(self, queryset, name, value) -> QuerySet:
|
||||||
@@ -254,7 +258,10 @@ class GeoReferencedTableFilterMixin(django_filters.FilterSet):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
value = value.replace("-", "")
|
value = value.replace("-", "")
|
||||||
queryset = self._filter_parcel_reference(queryset, name, value, "flrstck_nnr")
|
queryset = self._filter_parcel_reference(
|
||||||
|
queryset,
|
||||||
|
Q(flrstck_nnr=value),
|
||||||
|
)
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,8 @@ class BaseForm(forms.Form):
|
|||||||
self.has_required_fields = True
|
self.has_required_fields = True
|
||||||
break
|
break
|
||||||
|
|
||||||
|
self.check_for_recorded_instance()
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def save(self):
|
def save(self):
|
||||||
# To be implemented in subclasses!
|
# To be implemented in subclasses!
|
||||||
@@ -136,6 +138,38 @@ class BaseForm(forms.Form):
|
|||||||
set_class = set_class.replace(cls, "")
|
set_class = set_class.replace(cls, "")
|
||||||
self.fields[field].widget.attrs["class"] = set_class
|
self.fields[field].widget.attrs["class"] = set_class
|
||||||
|
|
||||||
|
def check_for_recorded_instance(self):
|
||||||
|
""" Checks if the instance is recorded and runs some special logic if yes
|
||||||
|
|
||||||
|
If the instance is recorded, the form shall not display any possibility to
|
||||||
|
edit any data. Instead, the users should get some information about why they can not edit anything.
|
||||||
|
|
||||||
|
There are situations where the form should be rendered regularly,
|
||||||
|
e.g deduction forms for (recorded) eco accounts.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
from intervention.forms.modalForms import NewDeductionModalForm, EditEcoAccountDeductionModalForm, \
|
||||||
|
RemoveEcoAccountDeductionModalForm
|
||||||
|
is_none = self.instance is None
|
||||||
|
is_other_data_type = not isinstance(self.instance, BaseObject)
|
||||||
|
is_deduction_form = isinstance(
|
||||||
|
self,
|
||||||
|
(
|
||||||
|
NewDeductionModalForm,
|
||||||
|
EditEcoAccountDeductionModalForm,
|
||||||
|
RemoveEcoAccountDeductionModalForm,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if is_none or is_other_data_type or is_deduction_form:
|
||||||
|
# Do nothing
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.instance.is_recorded:
|
||||||
|
self.template = "form/recorded_no_edit.html"
|
||||||
|
|
||||||
|
|
||||||
class RemoveForm(BaseForm):
|
class RemoveForm(BaseForm):
|
||||||
check = forms.BooleanField(
|
check = forms.BooleanField(
|
||||||
@@ -410,7 +444,6 @@ class NewDocumentModalForm(BaseModalForm):
|
|||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.form_title = _("Add new document")
|
self.form_title = _("Add new document")
|
||||||
self.form_caption = _("")
|
self.form_caption = _("")
|
||||||
self.template = "modal/modal_form.html"
|
|
||||||
self.form_attrs = {
|
self.form_attrs = {
|
||||||
"enctype": "multipart/form-data", # important for file upload
|
"enctype": "multipart/form-data", # important for file upload
|
||||||
}
|
}
|
||||||
@@ -597,4 +630,12 @@ class RecordModalForm(BaseModalForm):
|
|||||||
self.instance.set_unrecorded(self.user)
|
self.instance.set_unrecorded(self.user)
|
||||||
else:
|
else:
|
||||||
self.instance.set_recorded(self.user)
|
self.instance.set_recorded(self.user)
|
||||||
return self.instance
|
return self.instance
|
||||||
|
|
||||||
|
def check_for_recorded_instance(self):
|
||||||
|
""" Overwrite the check method for doing nothing on the RecordModalForm
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from compensation.models import CompensationState, Compensation, EcoAccount, Com
|
|||||||
from ema.models import Ema
|
from ema.models import Ema
|
||||||
from intervention.models import Intervention
|
from intervention.models import Intervention
|
||||||
from konova.management.commands.setup import BaseKonovaCommand
|
from konova.management.commands.setup import BaseKonovaCommand
|
||||||
from konova.models import Deadline, Geometry, Parcel, District
|
from konova.models import Deadline, Geometry, Parcel, District, Municipal, ParcelGroup
|
||||||
from user.models import UserActionLogEntry, UserAction
|
from user.models import UserActionLogEntry, UserAction
|
||||||
|
|
||||||
|
|
||||||
@@ -271,13 +271,26 @@ class Command(BaseKonovaCommand):
|
|||||||
self._write_success("No unused states found.")
|
self._write_success("No unused states found.")
|
||||||
self._break_line()
|
self._break_line()
|
||||||
|
|
||||||
|
def __sanitize_parcel_sub_type(self, cls):
|
||||||
|
unrelated_entries = cls.objects.filter(
|
||||||
|
parcels=None,
|
||||||
|
)
|
||||||
|
num_unrelated_entries = unrelated_entries.count()
|
||||||
|
cls_name = cls.__name__
|
||||||
|
if num_unrelated_entries > 0:
|
||||||
|
self._write_error(f"Found {num_unrelated_entries} unrelated {cls_name} entries. Delete now...")
|
||||||
|
unrelated_entries.delete()
|
||||||
|
self._write_success(f"Unrelated {cls_name} deleted.")
|
||||||
|
else:
|
||||||
|
self._write_success(f"No unrelated {cls_name} found.")
|
||||||
|
|
||||||
def sanitize_parcels_and_districts(self):
|
def sanitize_parcels_and_districts(self):
|
||||||
""" Removes unattached parcels and districts
|
""" Removes unattached parcels and districts
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self._write_warning("=== Sanitize parcels and districts ===")
|
self._write_warning("=== Sanitize administrative spatial references ===")
|
||||||
unrelated_parcels = Parcel.objects.filter(
|
unrelated_parcels = Parcel.objects.filter(
|
||||||
geometries=None,
|
geometries=None,
|
||||||
)
|
)
|
||||||
@@ -289,16 +302,12 @@ class Command(BaseKonovaCommand):
|
|||||||
else:
|
else:
|
||||||
self._write_success("No unrelated parcels found.")
|
self._write_success("No unrelated parcels found.")
|
||||||
|
|
||||||
unrelated_districts = District.objects.filter(
|
sub_types = [
|
||||||
parcels=None,
|
District,
|
||||||
)
|
Municipal,
|
||||||
num_unrelated_districts = unrelated_districts.count()
|
ParcelGroup
|
||||||
if num_unrelated_districts > 0:
|
]
|
||||||
self._write_error(f"Found {num_unrelated_districts} unrelated district entries. Delete now...")
|
for sub_type in sub_types:
|
||||||
unrelated_districts.delete()
|
self.__sanitize_parcel_sub_type(sub_type)
|
||||||
self._write_success("Unrelated districts deleted.")
|
|
||||||
else:
|
|
||||||
self._write_success("No unrelated districts found.")
|
|
||||||
|
|
||||||
self._break_line()
|
|
||||||
|
|
||||||
|
self._break_line()
|
||||||
@@ -5,6 +5,10 @@ Contact: michel.peltriaux@sgdnord.rlp.de
|
|||||||
Created on: 04.01.22
|
Created on: 04.01.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from django.contrib.gis.db.models.functions import Area
|
||||||
|
|
||||||
from konova.management.commands.setup import BaseKonovaCommand
|
from konova.management.commands.setup import BaseKonovaCommand
|
||||||
from konova.models import Geometry, Parcel, District
|
from konova.models import Geometry, Parcel, District
|
||||||
|
|
||||||
@@ -23,12 +27,21 @@ class Command(BaseKonovaCommand):
|
|||||||
num_parcels_before = Parcel.objects.count()
|
num_parcels_before = Parcel.objects.count()
|
||||||
num_districts_before = District.objects.count()
|
num_districts_before = District.objects.count()
|
||||||
self._write_warning("=== Update parcels and districts ===")
|
self._write_warning("=== Update parcels and districts ===")
|
||||||
|
# Order geometries by size to process smaller once at first
|
||||||
geometries = Geometry.objects.all().exclude(
|
geometries = Geometry.objects.all().exclude(
|
||||||
geom=None
|
geom=None
|
||||||
|
).annotate(area=Area("geom")).order_by(
|
||||||
|
'area'
|
||||||
)
|
)
|
||||||
self._write_warning(f"Process parcels for {geometries.count()} geometry entries now ...")
|
self._write_warning(f"Process parcels for {geometries.count()} geometry entries now ...")
|
||||||
|
i = 0
|
||||||
|
num_geoms = geometries.count()
|
||||||
for geometry in geometries:
|
for geometry in geometries:
|
||||||
|
self._write_warning(f"--- {datetime.datetime.now()} Process {geometry.id} now ...")
|
||||||
geometry.update_parcels()
|
geometry.update_parcels()
|
||||||
|
self._write_warning(f"--- Processed {geometry.get_underlying_parcels().count()} underlying parcels")
|
||||||
|
i += 1
|
||||||
|
self._write_warning(f"--- {i}/{num_geoms} processed")
|
||||||
|
|
||||||
num_parcels_after = Parcel.objects.count()
|
num_parcels_after = Parcel.objects.count()
|
||||||
num_districts_after = District.objects.count()
|
num_districts_after = District.objects.count()
|
||||||
|
|||||||
71
konova/migrations/0006_auto_20220411_0835.py
Normal file
71
konova/migrations/0006_auto_20220411_0835.py
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
# Generated by Django 3.1.3 on 2022-04-11 06:35
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('konova', '0005_auto_20220216_0856'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Municipal',
|
||||||
|
fields=[
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||||
|
('key', models.IntegerField(blank=True, help_text='Represents Gemeindeschlüssel', null=True)),
|
||||||
|
('name', models.CharField(blank=True, help_text='Gemeinde', max_length=1000, null=True)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='district',
|
||||||
|
old_name='krs',
|
||||||
|
new_name='name',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='district',
|
||||||
|
name='gmnd',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='parcel',
|
||||||
|
name='gmrkng',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='district',
|
||||||
|
name='key',
|
||||||
|
field=models.IntegerField(blank=True, help_text='Represents Kreisschlüssel', null=True),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ParcelGroup',
|
||||||
|
fields=[
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||||
|
('key', models.IntegerField(blank=True, help_text='Represents Gemarkungsschlüssel', null=True)),
|
||||||
|
('name', models.CharField(blank=True, help_text='Gemarkung', max_length=1000, null=True)),
|
||||||
|
('municipal', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='konova.municipal')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='municipal',
|
||||||
|
name='district',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='konova.district'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='parcel',
|
||||||
|
name='municipal',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parcels', to='konova.municipal'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='parcel',
|
||||||
|
name='parcel_group',
|
||||||
|
field=models.ForeignKey(blank=True, help_text='Gemarkung', null=True, on_delete=django.db.models.deletion.SET_NULL, to='konova.parcelgroup'),
|
||||||
|
),
|
||||||
|
]
|
||||||
28
konova/migrations/0007_auto_20220411_0848.py
Normal file
28
konova/migrations/0007_auto_20220411_0848.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# Generated by Django 3.1.3 on 2022-04-11 06:48
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('konova', '0006_auto_20220411_0835'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='district',
|
||||||
|
name='key',
|
||||||
|
field=models.CharField(blank=True, help_text='Represents Kreisschlüssel', max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='municipal',
|
||||||
|
name='key',
|
||||||
|
field=models.CharField(blank=True, help_text='Represents Gemeindeschlüssel', max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='parcelgroup',
|
||||||
|
name='key',
|
||||||
|
field=models.CharField(blank=True, help_text='Represents Gemarkungsschlüssel', max_length=255, null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
48
konova/migrations/0008_auto_20220411_0914.py
Normal file
48
konova/migrations/0008_auto_20220411_0914.py
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# Generated by Django 3.1.3 on 2022-04-11 07:14
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('konova', '0007_auto_20220411_0848'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='municipal',
|
||||||
|
name='key',
|
||||||
|
field=models.CharField(blank=True, help_text='Represents Kreisschlüssel', max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='municipal',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(blank=True, help_text='Kreis', max_length=1000, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='parcel',
|
||||||
|
name='flr',
|
||||||
|
field=models.IntegerField(blank=True, help_text='Flur', null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='parcel',
|
||||||
|
name='flrstck_nnr',
|
||||||
|
field=models.IntegerField(blank=True, help_text='Flurstücksnenner', null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='parcel',
|
||||||
|
name='flrstck_zhlr',
|
||||||
|
field=models.IntegerField(blank=True, help_text='Flurstückszähler', null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='parcelgroup',
|
||||||
|
name='key',
|
||||||
|
field=models.CharField(blank=True, help_text='Represents Kreisschlüssel', max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='parcelgroup',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(blank=True, help_text='Kreis', max_length=1000, null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
19
konova/migrations/0009_auto_20220411_1004.py
Normal file
19
konova/migrations/0009_auto_20220411_1004.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 3.1.3 on 2022-04-11 08:04
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('konova', '0008_auto_20220411_0914'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='parcel',
|
||||||
|
name='parcel_group',
|
||||||
|
field=models.ForeignKey(blank=True, help_text='Gemarkung', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parcels', to='konova.parcelgroup'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -20,6 +20,9 @@ class Geometry(BaseResource):
|
|||||||
from konova.settings import DEFAULT_SRID
|
from konova.settings import DEFAULT_SRID
|
||||||
geom = MultiPolygonField(null=True, blank=True, srid=DEFAULT_SRID)
|
geom = MultiPolygonField(null=True, blank=True, srid=DEFAULT_SRID)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(self.id)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
self.check_for_conflicts()
|
self.check_for_conflicts()
|
||||||
@@ -99,7 +102,7 @@ class Geometry(BaseResource):
|
|||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from konova.models import Parcel, District, ParcelIntersection
|
from konova.models import Parcel, District, ParcelIntersection, Municipal, ParcelGroup
|
||||||
parcel_fetcher = ParcelWFSFetcher(
|
parcel_fetcher = ParcelWFSFetcher(
|
||||||
geometry_id=self.id,
|
geometry_id=self.id,
|
||||||
)
|
)
|
||||||
@@ -110,20 +113,38 @@ class Geometry(BaseResource):
|
|||||||
_now = timezone.now()
|
_now = timezone.now()
|
||||||
underlying_parcels = []
|
underlying_parcels = []
|
||||||
for result in fetched_parcels:
|
for result in fetched_parcels:
|
||||||
fetched_parcel = result[typename]
|
parcel_properties = result["properties"]
|
||||||
# There could be parcels which include the word 'Flur',
|
# There could be parcels which include the word 'Flur',
|
||||||
# which needs to be deleted and just keep the numerical values
|
# which needs to be deleted and just keep the numerical values
|
||||||
## THIS CAN BE REMOVED IN THE FUTURE, WHEN 'Flur' WON'T OCCUR ANYMORE!
|
## THIS CAN BE REMOVED IN THE FUTURE, WHEN 'Flur' WON'T OCCUR ANYMORE!
|
||||||
flr_val = fetched_parcel["ave:flur"].replace("Flur ", "")
|
flr_val = parcel_properties["flur"].replace("Flur ", "")
|
||||||
parcel_obj = Parcel.objects.get_or_create(
|
|
||||||
gmrkng=fetched_parcel["ave:gemarkung"],
|
|
||||||
flr=flr_val,
|
|
||||||
flrstck_nnr=fetched_parcel['ave:flstnrnen'],
|
|
||||||
flrstck_zhlr=fetched_parcel['ave:flstnrzae'],
|
|
||||||
)[0]
|
|
||||||
district = District.objects.get_or_create(
|
district = District.objects.get_or_create(
|
||||||
gmnd=fetched_parcel["ave:gemeinde"],
|
key=parcel_properties["kreisschl"],
|
||||||
krs=fetched_parcel["ave:kreis"],
|
name=parcel_properties["kreis"],
|
||||||
|
)[0]
|
||||||
|
municipal = Municipal.objects.get_or_create(
|
||||||
|
key=parcel_properties["gmdschl"],
|
||||||
|
name=parcel_properties["gemeinde"],
|
||||||
|
district=district,
|
||||||
|
)[0]
|
||||||
|
parcel_group = ParcelGroup.objects.get_or_create(
|
||||||
|
key=parcel_properties["gemaschl"],
|
||||||
|
name=parcel_properties["gemarkung"],
|
||||||
|
municipal=municipal,
|
||||||
|
)[0]
|
||||||
|
flrstck_nnr = parcel_properties['flstnrnen']
|
||||||
|
if not flrstck_nnr:
|
||||||
|
flrstck_nnr = None
|
||||||
|
flrstck_zhlr = parcel_properties['flstnrzae']
|
||||||
|
if not flrstck_zhlr:
|
||||||
|
flrstck_zhlr = None
|
||||||
|
parcel_obj = Parcel.objects.get_or_create(
|
||||||
|
district=district,
|
||||||
|
municipal=municipal,
|
||||||
|
parcel_group=parcel_group,
|
||||||
|
flr=flr_val,
|
||||||
|
flrstck_nnr=flrstck_nnr,
|
||||||
|
flrstck_zhlr=flrstck_zhlr,
|
||||||
)[0]
|
)[0]
|
||||||
parcel_obj.district = district
|
parcel_obj.district = district
|
||||||
parcel_obj.updated_on = _now
|
parcel_obj.updated_on = _now
|
||||||
@@ -155,9 +176,10 @@ class Geometry(BaseResource):
|
|||||||
parcels = self.parcels.filter(
|
parcels = self.parcels.filter(
|
||||||
parcelintersection__calculated_on__isnull=False,
|
parcelintersection__calculated_on__isnull=False,
|
||||||
).prefetch_related(
|
).prefetch_related(
|
||||||
"district"
|
"district",
|
||||||
|
"municipal",
|
||||||
).order_by(
|
).order_by(
|
||||||
"gmrkng",
|
"municipal__name",
|
||||||
)
|
)
|
||||||
|
|
||||||
return parcels
|
return parcels
|
||||||
|
|||||||
@@ -289,6 +289,8 @@ class RecordableObjectMixin(models.Model):
|
|||||||
from user.models import UserActionLogEntry
|
from user.models import UserActionLogEntry
|
||||||
if self.recorded:
|
if self.recorded:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
self.unshare_with_default_users()
|
||||||
action = UserActionLogEntry.get_recorded_action(user)
|
action = UserActionLogEntry.get_recorded_action(user)
|
||||||
self.recorded = action
|
self.recorded = action
|
||||||
self.save()
|
self.save()
|
||||||
@@ -335,6 +337,15 @@ class RecordableObjectMixin(models.Model):
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError("Implement this in the subclass!")
|
raise NotImplementedError("Implement this in the subclass!")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_recorded(self):
|
||||||
|
""" Getter for record status as property
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
return self.recorded is not None
|
||||||
|
|
||||||
|
|
||||||
class CheckableObjectMixin(models.Model):
|
class CheckableObjectMixin(models.Model):
|
||||||
# Checks - Refers to "Genehmigen" but optional
|
# Checks - Refers to "Genehmigen" but optional
|
||||||
@@ -608,6 +619,26 @@ class ShareableObjectMixin(models.Model):
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError("Must be implemented in subclasses!")
|
raise NotImplementedError("Must be implemented in subclasses!")
|
||||||
|
|
||||||
|
def unshare_with_default_users(self):
|
||||||
|
""" Removes all shared users from direct shared access which are only default group users
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
from konova.utils.user_checks import is_default_group_only
|
||||||
|
users = self.shared_users
|
||||||
|
cleaned_users = []
|
||||||
|
default_users = []
|
||||||
|
for user in users:
|
||||||
|
if not is_default_group_only(user):
|
||||||
|
cleaned_users.append(user)
|
||||||
|
else:
|
||||||
|
default_users.append(user)
|
||||||
|
self.share_with_user_list(cleaned_users)
|
||||||
|
|
||||||
|
for user in default_users:
|
||||||
|
celery_send_mail_shared_access_removed.delay(self.identifier, self.title, user.id)
|
||||||
|
|
||||||
|
|
||||||
class GeoReferencedMixin(models.Model):
|
class GeoReferencedMixin(models.Model):
|
||||||
geometry = models.ForeignKey("konova.Geometry", null=True, blank=True, on_delete=models.SET_NULL)
|
geometry = models.ForeignKey("konova.Geometry", null=True, blank=True, on_delete=models.SET_NULL)
|
||||||
|
|||||||
@@ -10,8 +10,64 @@ from django.db import models
|
|||||||
from konova.models import UuidModel
|
from konova.models import UuidModel
|
||||||
|
|
||||||
|
|
||||||
|
class AdministrativeSpatialReference(models.Model):
|
||||||
|
key = models.CharField(
|
||||||
|
max_length=255,
|
||||||
|
help_text="Represents Kreisschlüssel",
|
||||||
|
null=True,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
|
name = models.CharField(
|
||||||
|
max_length=1000,
|
||||||
|
help_text="Kreis",
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.name} ({self.key})"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def table_str(self):
|
||||||
|
return f"{self.name} ({self.key})"
|
||||||
|
|
||||||
|
|
||||||
|
class District(UuidModel, AdministrativeSpatialReference):
|
||||||
|
""" The model District refers to "Kreis"
|
||||||
|
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Municipal(UuidModel, AdministrativeSpatialReference):
|
||||||
|
""" The model Municipal refers to "Gemeinde"
|
||||||
|
|
||||||
|
"""
|
||||||
|
district = models.ForeignKey(
|
||||||
|
District,
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ParcelGroup(UuidModel, AdministrativeSpatialReference):
|
||||||
|
""" The model ParcelGroup refers to "Gemarkung", which is defined as a loose group of parcels
|
||||||
|
|
||||||
|
"""
|
||||||
|
municipal = models.ForeignKey(
|
||||||
|
Municipal,
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Parcel(UuidModel):
|
class Parcel(UuidModel):
|
||||||
""" The Parcel model holds administrative data on the covered properties.
|
""" The Parcel model holds administrative data on covered properties.
|
||||||
|
|
||||||
Due to the unique but relevant naming of the administrative data, we have to use these namings as field
|
Due to the unique but relevant naming of the administrative data, we have to use these namings as field
|
||||||
names in german. Any try to translate them to English result in strange or insufficient translations.
|
names in german. Any try to translate them to English result in strange or insufficient translations.
|
||||||
@@ -24,59 +80,34 @@ class Parcel(UuidModel):
|
|||||||
"""
|
"""
|
||||||
geometries = models.ManyToManyField("konova.Geometry", blank=True, related_name="parcels", through='ParcelIntersection')
|
geometries = models.ManyToManyField("konova.Geometry", blank=True, related_name="parcels", through='ParcelIntersection')
|
||||||
district = models.ForeignKey("konova.District", on_delete=models.SET_NULL, null=True, blank=True, related_name="parcels")
|
district = models.ForeignKey("konova.District", on_delete=models.SET_NULL, null=True, blank=True, related_name="parcels")
|
||||||
gmrkng = models.CharField(
|
municipal = models.ForeignKey("konova.Municipal", on_delete=models.SET_NULL, null=True, blank=True, related_name="parcels")
|
||||||
max_length=1000,
|
parcel_group = models.ForeignKey(
|
||||||
|
"konova.ParcelGroup",
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
help_text="Gemarkung",
|
help_text="Gemarkung",
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
|
related_name="parcels"
|
||||||
)
|
)
|
||||||
flrstck_nnr = models.CharField(
|
flr = models.IntegerField(
|
||||||
max_length=1000,
|
help_text="Flur",
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
)
|
||||||
|
flrstck_nnr = models.IntegerField(
|
||||||
help_text="Flurstücksnenner",
|
help_text="Flurstücksnenner",
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
)
|
)
|
||||||
flrstck_zhlr = models.CharField(
|
flrstck_zhlr = models.IntegerField(
|
||||||
max_length=1000,
|
|
||||||
help_text="Flurstückszähler",
|
help_text="Flurstückszähler",
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
)
|
)
|
||||||
flr = models.CharField(
|
|
||||||
max_length=1000,
|
|
||||||
help_text="Flur",
|
|
||||||
null=True,
|
|
||||||
blank=True,
|
|
||||||
)
|
|
||||||
updated_on = models.DateTimeField(auto_now_add=True)
|
updated_on = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.gmrkng} | {self.flr} | {self.flrstck_zhlr} | {self.flrstck_nnr}"
|
return f"{self.parcel_group} | {self.flr} | {self.flrstck_zhlr} | {self.flrstck_nnr}"
|
||||||
|
|
||||||
|
|
||||||
class District(UuidModel):
|
|
||||||
""" The model District holds more coarse information, such as Kreis, Verbandsgemeinde and Gemeinde.
|
|
||||||
|
|
||||||
There might be the case that a geometry lies on a hundred Parcel entries but only on one District entry.
|
|
||||||
Therefore a geometry can have a lot of relations to Parcel entries but only a few or only a single one to one
|
|
||||||
District.
|
|
||||||
|
|
||||||
"""
|
|
||||||
gmnd = models.CharField(
|
|
||||||
max_length=1000,
|
|
||||||
help_text="Gemeinde",
|
|
||||||
null=True,
|
|
||||||
blank=True,
|
|
||||||
)
|
|
||||||
krs = models.CharField(
|
|
||||||
max_length=1000,
|
|
||||||
help_text="Kreis",
|
|
||||||
null=True,
|
|
||||||
blank=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.gmnd} | {self.krs}"
|
|
||||||
|
|
||||||
|
|
||||||
class ParcelIntersection(UuidModel):
|
class ParcelIntersection(UuidModel):
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ https://docs.djangoproject.com/en/3.1/ref/settings/
|
|||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from django.conf.locale.de import formats as de_formats
|
||||||
|
|
||||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||||
BASE_DIR = os.path.dirname(
|
BASE_DIR = os.path.dirname(
|
||||||
@@ -162,9 +163,15 @@ LANGUAGES = [
|
|||||||
|
|
||||||
USE_THOUSAND_SEPARATOR = True
|
USE_THOUSAND_SEPARATOR = True
|
||||||
|
|
||||||
|
# Regular python relevant date/datetime formatting
|
||||||
DEFAULT_DATE_TIME_FORMAT = '%d.%m.%Y %H:%M:%S'
|
DEFAULT_DATE_TIME_FORMAT = '%d.%m.%Y %H:%M:%S'
|
||||||
DEFAULT_DATE_FORMAT = '%d.%m.%Y'
|
DEFAULT_DATE_FORMAT = '%d.%m.%Y'
|
||||||
|
|
||||||
|
# Template relevant date/datetime formatting
|
||||||
|
# See the Note on here: https://docs.djangoproject.com/en/3.2/ref/templates/builtins/#date
|
||||||
|
de_formats.DATETIME_FORMAT = "d.m.Y, H:i"
|
||||||
|
de_formats.DATE_FORMAT = "d.m.Y"
|
||||||
|
|
||||||
TIME_ZONE = 'Europe/Berlin'
|
TIME_ZONE = 'Europe/Berlin'
|
||||||
|
|
||||||
USE_I18N = True
|
USE_I18N = True
|
||||||
|
|||||||
@@ -19,6 +19,6 @@ PAGE_SIZE_OPTIONS_TUPLES = [
|
|||||||
(50, 50),
|
(50, 50),
|
||||||
(100, 100),
|
(100, 100),
|
||||||
]
|
]
|
||||||
PAGE_SIZE_DEFAULT = 5
|
PAGE_SIZE_DEFAULT = 10
|
||||||
PAGE_SIZE_MAX = 100
|
PAGE_SIZE_MAX = 100
|
||||||
PAGE_DEFAULT = 1
|
PAGE_DEFAULT = 1
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
{% load i18n %}
|
|
||||||
<div class="table-container w-100 scroll-300">
|
|
||||||
{% if parcels|length == 0 %}
|
|
||||||
<article class="alert alert-info">
|
|
||||||
{% trans 'Parcels can not be calculated, since no geometry is given.' %}
|
|
||||||
</article>
|
|
||||||
{% else %}
|
|
||||||
<table class="table table-hover">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th scope="col">{% trans 'Kreis' %}</th>
|
|
||||||
<th scope="col">{% trans 'Gemarkung' %}</th>
|
|
||||||
<th scope="col">{% trans 'Parcel' %}</th>
|
|
||||||
<th scope="col">{% trans 'Parcel counter' %}</th>
|
|
||||||
<th scope="col">{% trans 'Parcel number' %}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for parcel in parcels %}
|
|
||||||
<tr>
|
|
||||||
<td>{{parcel.district.krs|default_if_none:"-"}}</td>
|
|
||||||
<td>{{parcel.gmrkng|default_if_none:"-"}}</td>
|
|
||||||
<td>{{parcel.flr|default_if_none:"-"}}</td>
|
|
||||||
<td>{{parcel.flrstck_zhlr|default_if_none:"-"}}</td>
|
|
||||||
<td>{{parcel.flrstck_nnr|default_if_none:"-"}}</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
{% load l10n i18n %}
|
||||||
|
{% for parcel in parcels %}
|
||||||
|
{% if forloop.last and next_page %}
|
||||||
|
<tr hx-get="{% url 'geometry-parcels-content' geom_id next_page %}"
|
||||||
|
hx-trigger="intersect once"
|
||||||
|
hx-swap="afterend">
|
||||||
|
<td>{{parcel.parcel_group.name|default_if_none:"-"}}</td>
|
||||||
|
<td>{{parcel.parcel_group.key|default_if_none:"-"}}</td>
|
||||||
|
<td>{{parcel.flr|default_if_none:"-"|unlocalize}}</td>
|
||||||
|
<td>{{parcel.flrstck_zhlr|default_if_none:"-"|unlocalize}}</td>
|
||||||
|
<td>{{parcel.flrstck_nnr|default_if_none:"-"|unlocalize}}</td>
|
||||||
|
</tr>
|
||||||
|
{% else %}
|
||||||
|
<tr>
|
||||||
|
<td>{{parcel.parcel_group.name|default_if_none:"-"}}</td>
|
||||||
|
<td>{{parcel.parcel_group.key|default_if_none:"-"}}</td>
|
||||||
|
<td>{{parcel.flr|default_if_none:"-"|unlocalize}}</td>
|
||||||
|
<td>{{parcel.flrstck_zhlr|default_if_none:"-"|unlocalize}}</td>
|
||||||
|
<td>{{parcel.flrstck_nnr|default_if_none:"-"|unlocalize}}</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
{% load i18n l10n %}
|
||||||
|
<div class="table-container w-100 scroll-300">
|
||||||
|
{% if parcels|length == 0 %}
|
||||||
|
<article class="alert alert-info">
|
||||||
|
{% trans 'Parcels can not be calculated, since no geometry is given.' %}
|
||||||
|
</article>
|
||||||
|
{% else %}
|
||||||
|
<table id="upper-spatial-table" class="table table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">{% trans 'Municipal' %}</th>
|
||||||
|
<th scope="col">{% trans 'Municipal key' %}</th>
|
||||||
|
<th scope="col">{% trans 'District' %}</th>
|
||||||
|
<th scope="col">{% trans 'District key' %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for municipal in municipals %}
|
||||||
|
<tr>
|
||||||
|
<td>{{municipal.name}}</td>
|
||||||
|
<td>{{municipal.key|unlocalize}}</td>
|
||||||
|
<td>{{municipal.district.name}}</td>
|
||||||
|
<td>{{municipal.district.key|unlocalize}}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table id="lower-spatial-table" class="table table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">{% trans 'Parcel group' %}</th>
|
||||||
|
<th scope="col">{% trans 'Parcel group key' %}</th>
|
||||||
|
<th scope="col">{% trans 'Parcel' %}</th>
|
||||||
|
<th scope="col">{% trans 'Parcel counter' %}</th>
|
||||||
|
<th scope="col">{% trans 'Parcel number' %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% include 'konova/includes/parcels/parcel_table_content.html' %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
</h5>
|
</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div hx-trigger="every 2s" hx-get="{% url 'geometry-parcels' geom_form.instance.geometry.id %}" title="{% trans 'Loading...' %}">
|
<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>
|
||||||
19
konova/templates/konova/includes/report/qrcodes.html
Normal file
19
konova/templates/konova/includes/report/qrcodes.html
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
|
||||||
|
<div class="col-sm-6 col-md-6 col-lg-6">
|
||||||
|
<button class="btn btn-outline-default col-sm-12">
|
||||||
|
<a href="{{qrcode.url}}" target="_blank">
|
||||||
|
<h4>{% trans 'Open in browser' %}</h4>
|
||||||
|
{{ qrcode.img|safe }}
|
||||||
|
</a>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6 col-md-6 col-lg-6">
|
||||||
|
<button class="btn btn-outline-default col-sm-12">
|
||||||
|
<a href="{{qrcode_lanis.url}}" target="_blank">
|
||||||
|
<h4>{% trans 'View in LANIS' %}</h4>
|
||||||
|
{{ qrcode_lanis.img|safe }}
|
||||||
|
</a>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
@@ -18,7 +18,7 @@ from django.urls import reverse
|
|||||||
|
|
||||||
from codelist.models import KonovaCode, KonovaCodeList
|
from codelist.models import KonovaCode, KonovaCodeList
|
||||||
from compensation.models import Compensation, CompensationState, CompensationAction, EcoAccount, EcoAccountDeduction
|
from compensation.models import Compensation, CompensationState, CompensationAction, EcoAccount, EcoAccountDeduction
|
||||||
from intervention.models import Legal, Responsibility, Intervention
|
from intervention.models import Legal, Responsibility, Intervention, Handler
|
||||||
from konova.management.commands.setup_data import GROUPS_DATA
|
from konova.management.commands.setup_data import GROUPS_DATA
|
||||||
from konova.models import Geometry
|
from konova.models import Geometry
|
||||||
from konova.settings import DEFAULT_GROUP
|
from konova.settings import DEFAULT_GROUP
|
||||||
@@ -57,6 +57,7 @@ class BaseTestCase(TestCase):
|
|||||||
|
|
||||||
self.create_users()
|
self.create_users()
|
||||||
self.create_groups()
|
self.create_groups()
|
||||||
|
self.handler = self.create_dummy_handler()
|
||||||
self.intervention = self.create_dummy_intervention()
|
self.intervention = self.create_dummy_intervention()
|
||||||
self.compensation = self.create_dummy_compensation()
|
self.compensation = self.create_dummy_compensation()
|
||||||
self.eco_account = self.create_dummy_eco_account()
|
self.eco_account = self.create_dummy_eco_account()
|
||||||
@@ -122,7 +123,9 @@ class BaseTestCase(TestCase):
|
|||||||
# Create legal data object (without M2M laws first)
|
# Create legal data object (without M2M laws first)
|
||||||
legal_data = Legal.objects.create()
|
legal_data = Legal.objects.create()
|
||||||
# Create responsible data object
|
# Create responsible data object
|
||||||
responsibility_data = Responsibility.objects.create()
|
responsibility_data = Responsibility.objects.create(
|
||||||
|
handler=self.handler
|
||||||
|
)
|
||||||
geometry = Geometry.objects.create()
|
geometry = Geometry.objects.create()
|
||||||
# Finally create main object, holding the other objects
|
# Finally create main object, holding the other objects
|
||||||
intervention = Intervention.objects.create(
|
intervention = Intervention.objects.create(
|
||||||
@@ -173,6 +176,9 @@ class BaseTestCase(TestCase):
|
|||||||
# Create responsible data object
|
# Create responsible data object
|
||||||
lega_data = Legal.objects.create()
|
lega_data = Legal.objects.create()
|
||||||
responsible_data = Responsibility.objects.create()
|
responsible_data = Responsibility.objects.create()
|
||||||
|
handler = self.handler
|
||||||
|
responsible_data.handler = handler
|
||||||
|
responsible_data.save()
|
||||||
# Finally create main object, holding the other objects
|
# Finally create main object, holding the other objects
|
||||||
eco_account = EcoAccount.objects.create(
|
eco_account = EcoAccount.objects.create(
|
||||||
identifier="TEST",
|
identifier="TEST",
|
||||||
@@ -197,6 +203,8 @@ class BaseTestCase(TestCase):
|
|||||||
geometry = Geometry.objects.create()
|
geometry = Geometry.objects.create()
|
||||||
# Create responsible data object
|
# Create responsible data object
|
||||||
responsible_data = Responsibility.objects.create()
|
responsible_data = Responsibility.objects.create()
|
||||||
|
responsible_data.handler = self.handler
|
||||||
|
responsible_data.save()
|
||||||
# Finally create main object, holding the other objects
|
# Finally create main object, holding the other objects
|
||||||
ema = Ema.objects.create(
|
ema = Ema.objects.create(
|
||||||
identifier="TEST",
|
identifier="TEST",
|
||||||
@@ -282,6 +290,18 @@ class BaseTestCase(TestCase):
|
|||||||
polygon = polygon.transform(3857, clone=True)
|
polygon = polygon.transform(3857, clone=True)
|
||||||
return MultiPolygon(polygon, srid=3857) # 3857 is the default srid used for MultiPolygonField in the form
|
return MultiPolygon(polygon, srid=3857) # 3857 is the default srid used for MultiPolygonField in the form
|
||||||
|
|
||||||
|
def create_dummy_handler(self) -> Handler:
|
||||||
|
""" Creates a Handler
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
handler = Handler.objects.get_or_create(
|
||||||
|
type=KonovaCode.objects.all().first(),
|
||||||
|
detail="Test handler"
|
||||||
|
)[0]
|
||||||
|
return handler
|
||||||
|
|
||||||
def fill_out_intervention(self, intervention: Intervention) -> Intervention:
|
def fill_out_intervention(self, intervention: Intervention) -> Intervention:
|
||||||
""" Adds all required (dummy) data to an intervention
|
""" Adds all required (dummy) data to an intervention
|
||||||
|
|
||||||
@@ -295,7 +315,7 @@ class BaseTestCase(TestCase):
|
|||||||
intervention.responsible.conservation_office = KonovaCode.objects.get(id=2)
|
intervention.responsible.conservation_office = KonovaCode.objects.get(id=2)
|
||||||
intervention.responsible.registration_file_number = "test"
|
intervention.responsible.registration_file_number = "test"
|
||||||
intervention.responsible.conservation_file_number = "test"
|
intervention.responsible.conservation_file_number = "test"
|
||||||
intervention.responsible.handler = "handler"
|
intervention.responsible.handler = self.handler
|
||||||
intervention.responsible.save()
|
intervention.responsible.save()
|
||||||
intervention.legal.registration_date = datetime.date.fromisoformat("1970-01-01")
|
intervention.legal.registration_date = datetime.date.fromisoformat("1970-01-01")
|
||||||
intervention.legal.binding_date = datetime.date.fromisoformat("1970-01-01")
|
intervention.legal.binding_date = datetime.date.fromisoformat("1970-01-01")
|
||||||
@@ -343,7 +363,7 @@ class BaseTestCase(TestCase):
|
|||||||
"""
|
"""
|
||||||
ema.responsible.conservation_office = self.get_conservation_office_code()
|
ema.responsible.conservation_office = self.get_conservation_office_code()
|
||||||
ema.responsible.conservation_file_number = "test"
|
ema.responsible.conservation_file_number = "test"
|
||||||
ema.responsible.handler = "handler"
|
ema.responsible.handler = self.handler
|
||||||
ema.responsible.save()
|
ema.responsible.save()
|
||||||
ema.after_states.add(self.comp_state)
|
ema.after_states.add(self.comp_state)
|
||||||
ema.before_states.add(self.comp_state)
|
ema.before_states.add(self.comp_state)
|
||||||
@@ -361,7 +381,7 @@ class BaseTestCase(TestCase):
|
|||||||
eco_account.legal.save()
|
eco_account.legal.save()
|
||||||
eco_account.responsible.conservation_office = self.get_conservation_office_code()
|
eco_account.responsible.conservation_office = self.get_conservation_office_code()
|
||||||
eco_account.responsible.conservation_file_number = "test"
|
eco_account.responsible.conservation_file_number = "test"
|
||||||
eco_account.responsible.handler = "handler"
|
eco_account.responsible.handler = self.handler
|
||||||
eco_account.responsible.save()
|
eco_account.responsible.save()
|
||||||
eco_account.after_states.add(self.comp_state)
|
eco_account.after_states.add(self.comp_state)
|
||||||
eco_account.before_states.add(self.comp_state)
|
eco_account.before_states.add(self.comp_state)
|
||||||
|
|||||||
@@ -20,10 +20,11 @@ from django.urls import path, include
|
|||||||
from konova.autocompletes import EcoAccountAutocomplete, \
|
from konova.autocompletes import EcoAccountAutocomplete, \
|
||||||
InterventionAutocomplete, CompensationActionCodeAutocomplete, BiotopeCodeAutocomplete, LawCodeAutocomplete, \
|
InterventionAutocomplete, CompensationActionCodeAutocomplete, BiotopeCodeAutocomplete, LawCodeAutocomplete, \
|
||||||
RegistrationOfficeCodeAutocomplete, ConservationOfficeCodeAutocomplete, ProcessTypeCodeAutocomplete, \
|
RegistrationOfficeCodeAutocomplete, ConservationOfficeCodeAutocomplete, ProcessTypeCodeAutocomplete, \
|
||||||
ShareUserAutocomplete, BiotopeExtraCodeAutocomplete, CompensationActionDetailCodeAutocomplete, ShareTeamAutocomplete
|
ShareUserAutocomplete, BiotopeExtraCodeAutocomplete, CompensationActionDetailCodeAutocomplete, \
|
||||||
|
ShareTeamAutocomplete, HandlerCodeAutocomplete
|
||||||
from konova.settings import SSO_SERVER, SSO_PUBLIC_KEY, SSO_PRIVATE_KEY, DEBUG
|
from konova.settings import SSO_SERVER, SSO_PUBLIC_KEY, SSO_PRIVATE_KEY, DEBUG
|
||||||
from konova.sso.sso import KonovaSSOClient
|
from konova.sso.sso import KonovaSSOClient
|
||||||
from konova.views import logout_view, home_view, get_geom_parcels
|
from konova.views import logout_view, home_view, get_geom_parcels, get_geom_parcels_content
|
||||||
|
|
||||||
sso_client = KonovaSSOClient(SSO_SERVER, SSO_PUBLIC_KEY, SSO_PRIVATE_KEY)
|
sso_client = KonovaSSOClient(SSO_SERVER, SSO_PUBLIC_KEY, SSO_PRIVATE_KEY)
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
@@ -39,7 +40,8 @@ urlpatterns = [
|
|||||||
path('cl/', include("codelist.urls")),
|
path('cl/', include("codelist.urls")),
|
||||||
path('analysis/', include("analysis.urls")),
|
path('analysis/', include("analysis.urls")),
|
||||||
path('api/', include("api.urls")),
|
path('api/', include("api.urls")),
|
||||||
path('geom/<id>/parcels', get_geom_parcels, name="geometry-parcels"),
|
path('geom/<id>/parcels/', get_geom_parcels, name="geometry-parcels"),
|
||||||
|
path('geom/<id>/parcels/<int:page>', get_geom_parcels_content, name="geometry-parcels-content"),
|
||||||
|
|
||||||
# Autocomplete paths for all apps
|
# Autocomplete paths for all apps
|
||||||
path("atcmplt/eco-accounts", EcoAccountAutocomplete.as_view(), name="accounts-autocomplete"),
|
path("atcmplt/eco-accounts", EcoAccountAutocomplete.as_view(), name="accounts-autocomplete"),
|
||||||
@@ -52,6 +54,7 @@ urlpatterns = [
|
|||||||
path("atcmplt/codes/prc-type", ProcessTypeCodeAutocomplete.as_view(), name="codes-process-type-autocomplete"),
|
path("atcmplt/codes/prc-type", ProcessTypeCodeAutocomplete.as_view(), name="codes-process-type-autocomplete"),
|
||||||
path("atcmplt/codes/reg-off", RegistrationOfficeCodeAutocomplete.as_view(), name="codes-registration-office-autocomplete"),
|
path("atcmplt/codes/reg-off", RegistrationOfficeCodeAutocomplete.as_view(), name="codes-registration-office-autocomplete"),
|
||||||
path("atcmplt/codes/cons-off", ConservationOfficeCodeAutocomplete.as_view(), name="codes-conservation-office-autocomplete"),
|
path("atcmplt/codes/cons-off", ConservationOfficeCodeAutocomplete.as_view(), name="codes-conservation-office-autocomplete"),
|
||||||
|
path("atcmplt/codes/handler", HandlerCodeAutocomplete.as_view(), name="codes-handler-autocomplete"),
|
||||||
path("atcmplt/share/u", ShareUserAutocomplete.as_view(), name="share-user-autocomplete"),
|
path("atcmplt/share/u", ShareUserAutocomplete.as_view(), name="share-user-autocomplete"),
|
||||||
path("atcmplt/share/t", ShareTeamAutocomplete.as_view(), name="share-team-autocomplete"),
|
path("atcmplt/share/t", ShareTeamAutocomplete.as_view(), name="share-team-autocomplete"),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ Created on: 02.08.21
|
|||||||
"""
|
"""
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
NO_DETAILS = _("no further details")
|
||||||
|
UNKNOWN = _("Unknown")
|
||||||
UNGROUPED = _("Ungrouped")
|
UNGROUPED = _("Ungrouped")
|
||||||
FORM_INVALID = _("There was an error on this form.")
|
FORM_INVALID = _("There was an error on this form.")
|
||||||
PARAMS_INVALID = _("Invalid parameters")
|
PARAMS_INVALID = _("Invalid parameters")
|
||||||
@@ -15,6 +17,7 @@ IDENTIFIER_REPLACED = _("The identifier '{}' had to be changed to '{}' since ano
|
|||||||
ENTRY_REMOVE_MISSING_PERMISSION = _("Only conservation or registration office users are allowed to remove entries.")
|
ENTRY_REMOVE_MISSING_PERMISSION = _("Only conservation or registration office users are allowed to remove entries.")
|
||||||
MISSING_GROUP_PERMISSION = _("You need to be part of another user group.")
|
MISSING_GROUP_PERMISSION = _("You need to be part of another user group.")
|
||||||
CHECKED_RECORDED_RESET = _("Status of Checked and Recorded reseted")
|
CHECKED_RECORDED_RESET = _("Status of Checked and Recorded reseted")
|
||||||
|
RECORDED_BLOCKS_EDIT = _("Entry is recorded. To edit data, the entry first needs to be unrecorded.")
|
||||||
|
|
||||||
# SHARE
|
# SHARE
|
||||||
DATA_UNSHARED = _("This data is not shared with you")
|
DATA_UNSHARED = _("This data is not shared with you")
|
||||||
|
|||||||
@@ -5,11 +5,12 @@ Contact: michel.peltriaux@sgdnord.rlp.de
|
|||||||
Created on: 17.12.21
|
Created on: 17.12.21
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
import json
|
||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
|
from json import JSONDecodeError
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
import xmltodict
|
|
||||||
from django.contrib.gis.db.models.functions import AsGML, Transform
|
from django.contrib.gis.db.models.functions import AsGML, Transform
|
||||||
from requests.auth import HTTPDigestAuth
|
from requests.auth import HTTPDigestAuth
|
||||||
|
|
||||||
@@ -115,7 +116,7 @@ class ParcelWFSFetcher(AbstractWFSFetcher):
|
|||||||
geometry_operation,
|
geometry_operation,
|
||||||
filter_srid
|
filter_srid
|
||||||
)
|
)
|
||||||
_filter = f'<wfs:GetFeature service="WFS" version="{self.version}" xmlns:wfs="http://www.opengis.net/wfs/2.0" xmlns:fes="http://www.opengis.net/fes/2.0" xmlns:myns="http://www.someserver.com/myns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wfs/2.0 http://schemas.opengis.net/wfs/2.0.0/wfs.xsd" count="{self.count}" startindex="{start_index}"><wfs:Query typeNames="{typenames}">{spatial_filter}</wfs:Query></wfs:GetFeature>'
|
_filter = f'<wfs:GetFeature service="WFS" version="{self.version}" xmlns:wfs="http://www.opengis.net/wfs/2.0" xmlns:fes="http://www.opengis.net/fes/2.0" xmlns:myns="http://www.someserver.com/myns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wfs/2.0 http://schemas.opengis.net/wfs/2.0.0/wfs.xsd" count="{self.count}" startindex="{start_index}" outputFormat="application/json; subtype=geojson"><wfs:Query typeNames="{typenames}">{spatial_filter}</wfs:Query></wfs:GetFeature>'
|
||||||
return _filter
|
return _filter
|
||||||
|
|
||||||
def get_features(self,
|
def get_features(self,
|
||||||
@@ -139,7 +140,7 @@ class ParcelWFSFetcher(AbstractWFSFetcher):
|
|||||||
Returns:
|
Returns:
|
||||||
features (list): A list of returned features
|
features (list): A list of returned features
|
||||||
"""
|
"""
|
||||||
features = []
|
found_features = []
|
||||||
while start_index is not None:
|
while start_index is not None:
|
||||||
post_body = self._create_post_data(
|
post_body = self._create_post_data(
|
||||||
spatial_operator,
|
spatial_operator,
|
||||||
@@ -155,19 +156,11 @@ class ParcelWFSFetcher(AbstractWFSFetcher):
|
|||||||
)
|
)
|
||||||
|
|
||||||
content = response.content.decode("utf-8")
|
content = response.content.decode("utf-8")
|
||||||
content = xmltodict.parse(content)
|
try:
|
||||||
collection = content.get(
|
# Check if collection is an exception and does not contain the requested data
|
||||||
"wfs:FeatureCollection",
|
content = json.loads(content)
|
||||||
{},
|
except JSONDecodeError as e:
|
||||||
)
|
if rerun_on_exception:
|
||||||
|
|
||||||
# Check if collection is an exception and does not contain the requested data
|
|
||||||
if len(collection) == 0:
|
|
||||||
exception = content.get(
|
|
||||||
"ows:ExceptionReport",
|
|
||||||
{}
|
|
||||||
)
|
|
||||||
if len(exception) > 0 and rerun_on_exception:
|
|
||||||
# Wait a second before another try
|
# Wait a second before another try
|
||||||
sleep(1)
|
sleep(1)
|
||||||
self.get_features(
|
self.get_features(
|
||||||
@@ -177,22 +170,21 @@ class ParcelWFSFetcher(AbstractWFSFetcher):
|
|||||||
start_index,
|
start_index,
|
||||||
rerun_on_exception=False
|
rerun_on_exception=False
|
||||||
)
|
)
|
||||||
|
|
||||||
members = collection.get(
|
|
||||||
"wfs:member",
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
if members is not None:
|
|
||||||
if len(members) > 1:
|
|
||||||
# extend feature list with found list of new feature members
|
|
||||||
features += members
|
|
||||||
else:
|
else:
|
||||||
# convert single found feature member into list and extent feature list
|
e.msg += content
|
||||||
features += [members]
|
raise e
|
||||||
|
fetched_features = content.get(
|
||||||
|
"features",
|
||||||
|
{},
|
||||||
|
)
|
||||||
|
|
||||||
if collection.get("@next", None) is not None:
|
found_features += fetched_features
|
||||||
start_index += self.count
|
|
||||||
else:
|
if len(fetched_features) < self.count:
|
||||||
|
# The response was not 'full', so we got everything to fetch
|
||||||
start_index = None
|
start_index = None
|
||||||
|
else:
|
||||||
|
# If a 'full' response returned, there might be more to fetch. Increase the start_index!
|
||||||
|
start_index += self.count
|
||||||
|
|
||||||
return features
|
return found_features
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ from compensation.models import Compensation, EcoAccount
|
|||||||
from intervention.models import Intervention
|
from intervention.models import Intervention
|
||||||
from konova.contexts import BaseContext
|
from konova.contexts import BaseContext
|
||||||
from konova.decorators import any_group_check
|
from konova.decorators import any_group_check
|
||||||
from konova.models import Deadline, Geometry
|
from konova.models import Deadline, Geometry, Municipal
|
||||||
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||||
from news.models import ServerMessage
|
from news.models import ServerMessage
|
||||||
from konova.settings import SSO_SERVER_BASE
|
from konova.settings import SSO_SERVER_BASE
|
||||||
@@ -110,12 +110,12 @@ def get_geom_parcels(request: HttpRequest, id: str):
|
|||||||
id (str): The geometry's id
|
id (str): The geometry's id
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
A rendered piece of HTML
|
||||||
"""
|
"""
|
||||||
# HTTP code 286 states that the HTMX should stop polling for updates
|
# HTTP code 286 states that the HTMX should stop polling for updates
|
||||||
# https://htmx.org/docs/#polling
|
# https://htmx.org/docs/#polling
|
||||||
status_code = 286
|
status_code = 286
|
||||||
template = "konova/includes/parcel_table.html"
|
template = "konova/includes/parcels/parcel_table_frame.html"
|
||||||
geom = get_object_or_404(Geometry, id=id)
|
geom = get_object_or_404(Geometry, id=id)
|
||||||
parcels = geom.get_underlying_parcels()
|
parcels = geom.get_underlying_parcels()
|
||||||
geos_geom = geom.geom
|
geos_geom = geom.geom
|
||||||
@@ -130,8 +130,21 @@ def get_geom_parcels(request: HttpRequest, id: str):
|
|||||||
status_code = 200
|
status_code = 200
|
||||||
|
|
||||||
if parcels_available or no_geometry_given:
|
if parcels_available or no_geometry_given:
|
||||||
|
parcels = parcels.order_by("-municipal", "flr", "flrstck_zhlr", "flrstck_nnr")
|
||||||
|
municipals = parcels.order_by("municipal").distinct("municipal").values("municipal__id")
|
||||||
|
municipals = Municipal.objects.filter(id__in=municipals)
|
||||||
|
|
||||||
|
rpp = 100
|
||||||
|
parcels = parcels[:rpp]
|
||||||
|
next_page = 1
|
||||||
|
if len(parcels) < rpp:
|
||||||
|
next_page = None
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
"parcels": parcels,
|
"parcels": parcels,
|
||||||
|
"municipals": municipals,
|
||||||
|
"geom_id": str(id),
|
||||||
|
"next_page": next_page,
|
||||||
}
|
}
|
||||||
html = render_to_string(template, context, request)
|
html = render_to_string(template, context, request)
|
||||||
return HttpResponse(html, status=status_code)
|
return HttpResponse(html, status=status_code)
|
||||||
@@ -139,6 +152,49 @@ def get_geom_parcels(request: HttpRequest, id: str):
|
|||||||
return HttpResponse(None, status=404)
|
return HttpResponse(None, status=404)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def get_geom_parcels_content(request: HttpRequest, id: str, page: int):
|
||||||
|
""" Getter for infinite scroll of HTMX
|
||||||
|
|
||||||
|
Returns parcels of a specific page/slice of the found parcel set.
|
||||||
|
Implementation of infinite scroll htmx example: https://htmx.org/examples/infinite-scroll/
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
id (str): The geometry's id
|
||||||
|
page (int): The requested page number
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A rendered piece of HTML
|
||||||
|
"""
|
||||||
|
if page < 0:
|
||||||
|
raise AssertionError("Parcel page can not be negative")
|
||||||
|
|
||||||
|
# HTTP code 286 states that the HTMX should stop polling for updates
|
||||||
|
# https://htmx.org/docs/#polling
|
||||||
|
status_code = 286
|
||||||
|
template = "konova/includes/parcels/parcel_table_content.html"
|
||||||
|
geom = get_object_or_404(Geometry, id=id)
|
||||||
|
parcels = geom.get_underlying_parcels()
|
||||||
|
|
||||||
|
parcels = parcels.order_by("-municipal", "flr", "flrstck_zhlr", "flrstck_nnr")
|
||||||
|
rpp = 100
|
||||||
|
from_p = rpp * (page-1)
|
||||||
|
to_p = rpp * (page)
|
||||||
|
next_page = page + 1
|
||||||
|
parcels = parcels[from_p:to_p]
|
||||||
|
if len(parcels) < rpp:
|
||||||
|
next_page = None
|
||||||
|
|
||||||
|
context = {
|
||||||
|
"parcels": parcels,
|
||||||
|
"geom_id": str(id),
|
||||||
|
"next_page": next_page,
|
||||||
|
}
|
||||||
|
html = render_to_string(template, context, request)
|
||||||
|
return HttpResponse(html, status=status_code)
|
||||||
|
|
||||||
|
|
||||||
def get_404_view(request: HttpRequest, exception=None):
|
def get_404_view(request: HttpRequest, exception=None):
|
||||||
""" Returns a 404 handling view
|
""" Returns a 404 handling view
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -28,6 +28,7 @@ kombu==5.2.3
|
|||||||
openpyxl==3.0.9
|
openpyxl==3.0.9
|
||||||
OWSLib==0.25.0
|
OWSLib==0.25.0
|
||||||
packaging==21.3
|
packaging==21.3
|
||||||
|
pika==1.2.0
|
||||||
prompt-toolkit==3.0.24
|
prompt-toolkit==3.0.24
|
||||||
psycopg2-binary==2.9.1
|
psycopg2-binary==2.9.1
|
||||||
pyparsing==3.0.6
|
pyparsing==3.0.6
|
||||||
|
|||||||
19
templates/form/recorded_no_edit.html
Normal file
19
templates/form/recorded_no_edit.html
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{% load i18n fontawesome_5 %}
|
||||||
|
|
||||||
|
<div class="p-5 col-sm-12">
|
||||||
|
<h4>
|
||||||
|
<span class="registered-bookmark">
|
||||||
|
{% fa5_icon 'bookmark' %}
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
{% trans 'This data is recorded' %}
|
||||||
|
</span>
|
||||||
|
</h4>
|
||||||
|
<hr>
|
||||||
|
<article>
|
||||||
|
{% blocktrans %}
|
||||||
|
Whilst recorded the data is published publicly. If you wish to edit any information on this data, the data needs
|
||||||
|
to be unrecorded first. Do not forget to record it afterwards, again.
|
||||||
|
{% endblocktrans %}
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
@@ -7,7 +7,9 @@
|
|||||||
<hr>
|
<hr>
|
||||||
<p class="lead">
|
<p class="lead">
|
||||||
{% blocktrans %}
|
{% blocktrans %}
|
||||||
The data you want to see is not recorded and might still be work in progress. Please come back another time.
|
The data you want to see is not recorded and might still be work in progress or the legal binding date has
|
||||||
|
not been reached yet. We can not publish this report as long as revocations could occur.
|
||||||
|
Please come back later.
|
||||||
{% endblocktrans %}
|
{% endblocktrans %}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -74,6 +74,15 @@ class TeamAdmin(admin.ModelAdmin):
|
|||||||
"name",
|
"name",
|
||||||
"description",
|
"description",
|
||||||
]
|
]
|
||||||
|
filter_horizontal = [
|
||||||
|
"users"
|
||||||
|
]
|
||||||
|
|
||||||
|
def formfield_for_foreignkey(self, db_field, request, **kwargs):
|
||||||
|
if db_field.name == "admin":
|
||||||
|
team_id = request.resolver_match.kwargs.get("object_id", None)
|
||||||
|
kwargs["queryset"] = User.objects.filter(teams__id__in=[team_id])
|
||||||
|
return super().formfield_for_foreignkey(db_field, request, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(User, UserAdmin)
|
admin.site.register(User, UserAdmin)
|
||||||
|
|||||||
@@ -317,6 +317,15 @@ class RemoveTeamModalForm(RemoveModalForm):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class LeaveTeamModalForm(RemoveModalForm):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.form_title = _("Leave team")
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
self.instance.remove_user(self.user)
|
||||||
|
|
||||||
|
|
||||||
class TeamDataForm(BaseModalForm):
|
class TeamDataForm(BaseModalForm):
|
||||||
name = forms.CharField(
|
name = forms.CharField(
|
||||||
label_suffix="",
|
label_suffix="",
|
||||||
|
|||||||
@@ -93,3 +93,17 @@ class Team(UuidModel):
|
|||||||
"""
|
"""
|
||||||
mailer = Mailer()
|
mailer = Mailer()
|
||||||
mailer.send_mail_shared_data_deleted_team(obj_identifier, obj_title, self)
|
mailer.send_mail_shared_data_deleted_team(obj_identifier, obj_title, self)
|
||||||
|
|
||||||
|
def remove_user(self, user):
|
||||||
|
""" Removes a user from the team
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user (User): The user to be removed
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.users.remove(user)
|
||||||
|
if self.admin == user:
|
||||||
|
self.admin = self.users.first()
|
||||||
|
self.save()
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import uuid
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from konova.sub_settings.django_settings import DEFAULT_DATE_FORMAT, DEFAULT_DATE_TIME_FORMAT
|
from konova.sub_settings.django_settings import DEFAULT_DATE_TIME_FORMAT
|
||||||
|
|
||||||
|
|
||||||
class UserAction(models.TextChoices):
|
class UserAction(models.TextChoices):
|
||||||
|
|||||||
@@ -54,14 +54,6 @@
|
|||||||
</button>
|
</button>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-2">
|
|
||||||
<a href="{% url 'user:api-token' %}" title="{% trans 'See or edit your API token' %}">
|
|
||||||
<button class="btn btn-default">
|
|
||||||
{% fa5_icon 'code' %}
|
|
||||||
<span>{% trans 'API' %}</span>
|
|
||||||
</button>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-2">
|
<div class="row mb-2">
|
||||||
<a href="{% url 'user:team-index' %}" title="{% trans 'Manage teams' %}">
|
<a href="{% url 'user:team-index' %}" title="{% trans 'Manage teams' %}">
|
||||||
<button class="btn btn-default">
|
<button class="btn btn-default">
|
||||||
@@ -70,6 +62,14 @@
|
|||||||
</button>
|
</button>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row mb-2">
|
||||||
|
<a href="{% url 'user:api-token' %}" title="{% trans 'See or edit your API token' %}">
|
||||||
|
<button class="btn btn-default">
|
||||||
|
{% fa5_icon 'code' %}
|
||||||
|
<span>{% trans 'API' %}</span>
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -46,6 +46,9 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
<button class="btn rlp-r btn-modal" data-form-url="{% url 'user:team-leave' team.id %}" title="{% trans 'Leave team' %}">
|
||||||
|
{% fa5_icon 'sign-out-alt' %}
|
||||||
|
</button>
|
||||||
{% if team.admin == user %}
|
{% if team.admin == user %}
|
||||||
<button class="btn rlp-r btn-modal" data-form-url="{% url 'user:team-edit' team.id %}" title="{% trans 'Edit team' %}">
|
<button class="btn rlp-r btn-modal" data-form-url="{% url 'user:team-edit' team.id %}" title="{% trans 'Edit team' %}">
|
||||||
{% fa5_icon 'edit' %}
|
{% fa5_icon 'edit' %}
|
||||||
|
|||||||
@@ -20,5 +20,6 @@ urlpatterns = [
|
|||||||
path("team/<id>", data_team_view, name="team-data"),
|
path("team/<id>", data_team_view, name="team-data"),
|
||||||
path("team/<id>/edit", edit_team_view, name="team-edit"),
|
path("team/<id>/edit", edit_team_view, name="team-edit"),
|
||||||
path("team/<id>/remove", remove_team_view, name="team-remove"),
|
path("team/<id>/remove", remove_team_view, name="team-remove"),
|
||||||
|
path("team/<id>/leave", leave_team_view, name="team-leave"),
|
||||||
|
|
||||||
]
|
]
|
||||||
@@ -13,7 +13,7 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
from konova.contexts import BaseContext
|
from konova.contexts import BaseContext
|
||||||
from konova.decorators import any_group_check, default_group_required
|
from konova.decorators import any_group_check, default_group_required
|
||||||
from user.forms import UserNotificationForm, UserContactForm, UserAPITokenForm, NewTeamModalForm, EditTeamModalForm, \
|
from user.forms import UserNotificationForm, UserContactForm, UserAPITokenForm, NewTeamModalForm, EditTeamModalForm, \
|
||||||
RemoveTeamModalForm, TeamDataForm
|
RemoveTeamModalForm, TeamDataForm, LeaveTeamModalForm
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@@ -204,3 +204,24 @@ def remove_team_view(request: HttpRequest, id: str):
|
|||||||
_("Team removed"),
|
_("Team removed"),
|
||||||
redirect_url=reverse("user:team-index")
|
redirect_url=reverse("user:team-index")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def leave_team_view(request: HttpRequest, id: str):
|
||||||
|
team = get_object_or_404(Team, id=id)
|
||||||
|
user = request.user
|
||||||
|
|
||||||
|
is_user_team_member = team.users.filter(id=user.id).exists()
|
||||||
|
if not is_user_team_member:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
_("You are not a member of this team")
|
||||||
|
)
|
||||||
|
return redirect("user:team-index")
|
||||||
|
|
||||||
|
form = LeaveTeamModalForm(request.POST or None, instance=team, request=request)
|
||||||
|
return form.process_request(
|
||||||
|
request,
|
||||||
|
_("Left Team"),
|
||||||
|
redirect_url=reverse("user:team-index")
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user