Compare commits
No commits in common. "dd5cbbef107b22a585cf211c67ff63cecdfbcef7" and "3c211e65608016fad94aec6662b87faa74b64ca4" have entirely different histories.
dd5cbbef10
...
3c211e6560
@ -6,7 +6,6 @@
|
|||||||
"title": "Test_compensation",
|
"title": "Test_compensation",
|
||||||
"is_cef": false,
|
"is_cef": false,
|
||||||
"is_coherence_keeping": false,
|
"is_coherence_keeping": false,
|
||||||
"is_pik": false,
|
|
||||||
"intervention": "MUST_BE_SET_IN_TEST",
|
"intervention": "MUST_BE_SET_IN_TEST",
|
||||||
"before_states": [
|
"before_states": [
|
||||||
],
|
],
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"title": "Test_ecoaccount",
|
"title": "Test_ecoaccount",
|
||||||
"deductable_surface": 10000.0,
|
"deductable_surface": 10000.0,
|
||||||
"is_pik": false,
|
|
||||||
"responsible": {
|
"responsible": {
|
||||||
"conservation_office": null,
|
"conservation_office": null,
|
||||||
"conservation_file_number": null,
|
"conservation_file_number": null,
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"title": "Test_ema",
|
"title": "Test_ema",
|
||||||
"is_pik": false,
|
|
||||||
"responsible": {
|
"responsible": {
|
||||||
"conservation_office": null,
|
"conservation_office": null,
|
||||||
"conservation_file_number": null,
|
"conservation_file_number": null,
|
||||||
|
@ -122,7 +122,6 @@ class APIV1GetTestCase(BaseAPIV1TestCase):
|
|||||||
props = geojson["properties"]
|
props = geojson["properties"]
|
||||||
props["is_cef"]
|
props["is_cef"]
|
||||||
props["is_coherence_keeping"]
|
props["is_coherence_keeping"]
|
||||||
props["is_pik"]
|
|
||||||
props["intervention"]
|
props["intervention"]
|
||||||
props["intervention"]["id"]
|
props["intervention"]["id"]
|
||||||
props["intervention"]["identifier"]
|
props["intervention"]["identifier"]
|
||||||
|
@ -46,7 +46,6 @@
|
|||||||
"title": "TEST_compensation_CHANGED",
|
"title": "TEST_compensation_CHANGED",
|
||||||
"is_cef": true,
|
"is_cef": true,
|
||||||
"is_coherence_keeping": true,
|
"is_coherence_keeping": true,
|
||||||
"is_pik": true,
|
|
||||||
"intervention": "CHANGE_BEFORE_RUN!!!",
|
"intervention": "CHANGE_BEFORE_RUN!!!",
|
||||||
"before_states": [],
|
"before_states": [],
|
||||||
"after_states": [],
|
"after_states": [],
|
||||||
|
@ -45,7 +45,6 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"title": "TEST_account_CHANGED",
|
"title": "TEST_account_CHANGED",
|
||||||
"deductable_surface": "100000.0",
|
"deductable_surface": "100000.0",
|
||||||
"is_pik": true,
|
|
||||||
"responsible": {
|
"responsible": {
|
||||||
"conservation_office": null,
|
"conservation_office": null,
|
||||||
"conservation_file_number": "123-TEST",
|
"conservation_file_number": "123-TEST",
|
||||||
|
@ -52,7 +52,6 @@
|
|||||||
"detail": "TEST_HANDLER_CHANGED"
|
"detail": "TEST_HANDLER_CHANGED"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"is_pik": true,
|
|
||||||
"before_states": [],
|
"before_states": [],
|
||||||
"after_states": [],
|
"after_states": [],
|
||||||
"actions": [],
|
"actions": [],
|
||||||
|
@ -97,7 +97,6 @@ class APIV1UpdateTestCase(BaseAPIV1TestCase):
|
|||||||
self.assertNotEqual(modified_on, self.compensation.modified)
|
self.assertNotEqual(modified_on, self.compensation.modified)
|
||||||
self.assertEqual(put_props["is_cef"], self.compensation.is_cef)
|
self.assertEqual(put_props["is_cef"], self.compensation.is_cef)
|
||||||
self.assertEqual(put_props["is_coherence_keeping"], self.compensation.is_coherence_keeping)
|
self.assertEqual(put_props["is_coherence_keeping"], self.compensation.is_coherence_keeping)
|
||||||
self.assertEqual(put_props["is_pik"], self.compensation.is_pik)
|
|
||||||
self.assertEqual(len(put_props["actions"]), self.compensation.actions.count())
|
self.assertEqual(len(put_props["actions"]), self.compensation.actions.count())
|
||||||
self.assertEqual(len(put_props["before_states"]), self.compensation.before_states.count())
|
self.assertEqual(len(put_props["before_states"]), self.compensation.before_states.count())
|
||||||
self.assertEqual(len(put_props["after_states"]), self.compensation.after_states.count())
|
self.assertEqual(len(put_props["after_states"]), self.compensation.after_states.count())
|
||||||
|
@ -34,7 +34,6 @@ class CompensationAPISerializerV1(AbstractModelAPISerializerV1, AbstractCompensa
|
|||||||
def _extend_properties_data(self, entry):
|
def _extend_properties_data(self, entry):
|
||||||
self.properties_data["is_cef"] = entry.is_cef
|
self.properties_data["is_cef"] = entry.is_cef
|
||||||
self.properties_data["is_coherence_keeping"] = entry.is_coherence_keeping
|
self.properties_data["is_coherence_keeping"] = entry.is_coherence_keeping
|
||||||
self.properties_data["is_pik"] = entry.is_pik
|
|
||||||
self.properties_data["intervention"] = self.intervention_to_json(entry.intervention)
|
self.properties_data["intervention"] = self.intervention_to_json(entry.intervention)
|
||||||
self.properties_data["before_states"] = self._compensation_state_to_json(entry.before_states.all())
|
self.properties_data["before_states"] = self._compensation_state_to_json(entry.before_states.all())
|
||||||
self.properties_data["after_states"] = self._compensation_state_to_json(entry.after_states.all())
|
self.properties_data["after_states"] = self._compensation_state_to_json(entry.after_states.all())
|
||||||
@ -114,7 +113,6 @@ class CompensationAPISerializerV1(AbstractModelAPISerializerV1, AbstractCompensa
|
|||||||
obj.title = properties["title"]
|
obj.title = properties["title"]
|
||||||
obj.is_cef = properties["is_cef"]
|
obj.is_cef = properties["is_cef"]
|
||||||
obj.is_coherence_keeping = properties["is_coherence_keeping"]
|
obj.is_coherence_keeping = properties["is_coherence_keeping"]
|
||||||
obj.is_pik = properties.get("is_pik", False)
|
|
||||||
obj = self.set_intervention(obj, properties["intervention"], user)
|
obj = self.set_intervention(obj, properties["intervention"], user)
|
||||||
|
|
||||||
obj.geometry.save()
|
obj.geometry.save()
|
||||||
@ -151,7 +149,6 @@ class CompensationAPISerializerV1(AbstractModelAPISerializerV1, AbstractCompensa
|
|||||||
obj.title = properties["title"]
|
obj.title = properties["title"]
|
||||||
obj.is_cef = properties["is_cef"]
|
obj.is_cef = properties["is_cef"]
|
||||||
obj.is_coherence_keeping = properties["is_coherence_keeping"]
|
obj.is_coherence_keeping = properties["is_coherence_keeping"]
|
||||||
obj.is_pik = properties.get("is_pik", False)
|
|
||||||
obj.modified = update_action
|
obj.modified = update_action
|
||||||
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
|
||||||
|
@ -25,7 +25,6 @@ class EcoAccountAPISerializerV1(AbstractModelAPISerializerV1,
|
|||||||
model = EcoAccount
|
model = EcoAccount
|
||||||
|
|
||||||
def _extend_properties_data(self, entry):
|
def _extend_properties_data(self, entry):
|
||||||
self.properties_data["is_pik"] = entry.is_pik
|
|
||||||
self.properties_data["deductable_surface"] = entry.deductable_surface
|
self.properties_data["deductable_surface"] = entry.deductable_surface
|
||||||
self.properties_data["deductable_surface_available"] = entry.deductable_surface - entry.get_deductions_surface()
|
self.properties_data["deductable_surface_available"] = entry.deductable_surface - entry.get_deductions_surface()
|
||||||
self.properties_data["responsible"] = self._responsible_to_json(entry.responsible)
|
self.properties_data["responsible"] = self._responsible_to_json(entry.responsible)
|
||||||
@ -123,7 +122,6 @@ class EcoAccountAPISerializerV1(AbstractModelAPISerializerV1,
|
|||||||
properties = json_model["properties"]
|
properties = json_model["properties"]
|
||||||
obj.identifier = obj.generate_new_identifier()
|
obj.identifier = obj.generate_new_identifier()
|
||||||
obj.title = properties["title"]
|
obj.title = properties["title"]
|
||||||
obj.is_pik = properties.get("is_pik", False)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
obj.deductable_surface = float(properties["deductable_surface"])
|
obj.deductable_surface = float(properties["deductable_surface"])
|
||||||
@ -171,7 +169,6 @@ class EcoAccountAPISerializerV1(AbstractModelAPISerializerV1,
|
|||||||
# Fill in data to objects
|
# Fill in data to objects
|
||||||
properties = json_model["properties"]
|
properties = json_model["properties"]
|
||||||
obj.title = properties["title"]
|
obj.title = properties["title"]
|
||||||
obj.is_pik = properties.get("is_pik", False)
|
|
||||||
obj.deductable_surface = float(properties["deductable_surface"])
|
obj.deductable_surface = float(properties["deductable_surface"])
|
||||||
obj.modified = update_action
|
obj.modified = update_action
|
||||||
obj.geometry.geom = self._create_geometry_from_json(json_model)
|
obj.geometry.geom = self._create_geometry_from_json(json_model)
|
||||||
|
@ -21,7 +21,6 @@ class EmaAPISerializerV1(AbstractModelAPISerializerV1, AbstractCompensationAPISe
|
|||||||
model = Ema
|
model = Ema
|
||||||
|
|
||||||
def _extend_properties_data(self, entry):
|
def _extend_properties_data(self, entry):
|
||||||
self.properties_data["is_pik"] = entry.is_pik
|
|
||||||
self.properties_data["responsible"] = self._responsible_to_json(entry.responsible)
|
self.properties_data["responsible"] = self._responsible_to_json(entry.responsible)
|
||||||
self.properties_data["before_states"] = self._compensation_state_to_json(entry.before_states.all())
|
self.properties_data["before_states"] = self._compensation_state_to_json(entry.before_states.all())
|
||||||
self.properties_data["after_states"] = self._compensation_state_to_json(entry.after_states.all())
|
self.properties_data["after_states"] = self._compensation_state_to_json(entry.after_states.all())
|
||||||
@ -105,7 +104,6 @@ class EmaAPISerializerV1(AbstractModelAPISerializerV1, AbstractCompensationAPISe
|
|||||||
properties = json_model["properties"]
|
properties = json_model["properties"]
|
||||||
obj.identifier = obj.generate_new_identifier()
|
obj.identifier = obj.generate_new_identifier()
|
||||||
obj.title = properties["title"]
|
obj.title = properties["title"]
|
||||||
obj.is_pik = properties.get("is_pik", False)
|
|
||||||
obj = self._set_responsibility(obj, properties["responsible"])
|
obj = self._set_responsibility(obj, properties["responsible"])
|
||||||
|
|
||||||
obj.geometry.save()
|
obj.geometry.save()
|
||||||
@ -143,7 +141,6 @@ class EmaAPISerializerV1(AbstractModelAPISerializerV1, AbstractCompensationAPISe
|
|||||||
# Fill in data to objects
|
# Fill in data to objects
|
||||||
properties = json_model["properties"]
|
properties = json_model["properties"]
|
||||||
obj.title = properties["title"]
|
obj.title = properties["title"]
|
||||||
obj.is_pik = properties.get("is_pik", False)
|
|
||||||
obj.modified = update_action
|
obj.modified = update_action
|
||||||
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
|
||||||
|
@ -132,7 +132,6 @@ 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):
|
||||||
@ -198,7 +197,7 @@ class InterventionAPISerializerV1(AbstractModelAPISerializerV1,
|
|||||||
obj.legal.save()
|
obj.legal.save()
|
||||||
obj.save()
|
obj.save()
|
||||||
|
|
||||||
obj.mark_as_edited(user, edit_comment="API update")
|
obj.mark_as_edited(user)
|
||||||
|
|
||||||
celery_update_parcels.delay(obj.geometry.id)
|
celery_update_parcels.delay(obj.geometry.id)
|
||||||
|
|
||||||
|
@ -75,10 +75,7 @@ class AbstractModelAPISerializerV1(AbstractModelAPISerializer):
|
|||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if json_str is None:
|
if json_str is None or len(json_str) == 0:
|
||||||
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,
|
||||||
|
@ -33,7 +33,6 @@ class KonovaCodeAdmin(admin.ModelAdmin):
|
|||||||
"is_selectable",
|
"is_selectable",
|
||||||
"is_leaf",
|
"is_leaf",
|
||||||
"parent",
|
"parent",
|
||||||
"found_in_codelists",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
search_fields = [
|
search_fields = [
|
||||||
@ -43,12 +42,6 @@ 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)
|
||||||
|
@ -65,23 +65,24 @@ class KonovaCode(models.Model):
|
|||||||
ret_val += ", " + self.parent.long_name
|
ret_val += ", " + self.parent.long_name
|
||||||
return ret_val
|
return ret_val
|
||||||
|
|
||||||
def add_children(self, order_by: str = "long_name"):
|
def add_children(self):
|
||||||
""" Adds all children (resurcively until leaf) as .children to the KonovaCode
|
""" Adds all children (resurcively until leaf) as .children to the KonovaCode
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
code (KonovaCode): The manipulated KonovaCode instance
|
code (KonovaCode): The manipulated KonovaCode instance
|
||||||
"""
|
"""
|
||||||
if self.is_leaf:
|
if self.is_leaf:
|
||||||
return self
|
return None
|
||||||
|
|
||||||
children = KonovaCode.objects.filter(
|
children = KonovaCode.objects.filter(
|
||||||
|
code_lists__in=self.code_lists.all(),
|
||||||
parent=self
|
parent=self
|
||||||
).order_by(
|
).order_by(
|
||||||
order_by
|
"long_name"
|
||||||
)
|
)
|
||||||
self.children = children
|
self.children = children
|
||||||
for child in children:
|
for child in children:
|
||||||
child.add_children(order_by)
|
child.add_children()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,30 +21,16 @@ class AbstractCompensationAdmin(BaseObjectAdmin):
|
|||||||
"identifier",
|
"identifier",
|
||||||
"title",
|
"title",
|
||||||
"comment",
|
"comment",
|
||||||
"list_after_states",
|
"after_states",
|
||||||
"list_before_states",
|
"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) + [
|
||||||
"list_after_states",
|
"after_states",
|
||||||
"list_before_states",
|
"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 = [
|
||||||
@ -55,7 +41,6 @@ class CompensationAdmin(AbstractCompensationAdmin):
|
|||||||
return super().get_fields(request, obj) + [
|
return super().get_fields(request, obj) + [
|
||||||
"is_cef",
|
"is_cef",
|
||||||
"is_coherence_keeping",
|
"is_coherence_keeping",
|
||||||
"is_pik",
|
|
||||||
"intervention",
|
"intervention",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ class CheckboxCompensationTableFilter(CheckboxTableFilter):
|
|||||||
if not value:
|
if not value:
|
||||||
return queryset.filter(
|
return queryset.filter(
|
||||||
Q(intervention__users__in=[self.user]) | # requesting user has access
|
Q(intervention__users__in=[self.user]) | # requesting user has access
|
||||||
Q(intervention__teams__in=self.user.shared_teams)
|
Q(intervention__teams__users__in=[self.user])
|
||||||
).distinct()
|
).distinct()
|
||||||
else:
|
else:
|
||||||
return queryset
|
return queryset
|
||||||
|
@ -160,23 +160,7 @@ class CoherenceCompensationFormMixin(forms.Form):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class PikCompensationFormMixin(forms.Form):
|
class NewCompensationForm(AbstractCompensationForm, CEFCompensationFormMixin, CoherenceCompensationFormMixin):
|
||||||
""" A form mixin, providing PIK compensation field
|
|
||||||
|
|
||||||
"""
|
|
||||||
is_pik = forms.BooleanField(
|
|
||||||
label_suffix="",
|
|
||||||
label=_("Is PIK"),
|
|
||||||
help_text=_("Optionally: Whether this compensation is a compensation integrated in production?"),
|
|
||||||
required=False,
|
|
||||||
widget=forms.CheckboxInput()
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class NewCompensationForm(AbstractCompensationForm,
|
|
||||||
CEFCompensationFormMixin,
|
|
||||||
CoherenceCompensationFormMixin,
|
|
||||||
PikCompensationFormMixin):
|
|
||||||
""" Form for creating new compensations.
|
""" Form for creating new compensations.
|
||||||
|
|
||||||
Can be initialized with an intervention id for preselecting the related intervention.
|
Can be initialized with an intervention id for preselecting the related intervention.
|
||||||
@ -207,7 +191,6 @@ class NewCompensationForm(AbstractCompensationForm,
|
|||||||
"identifier",
|
"identifier",
|
||||||
"title",
|
"title",
|
||||||
"intervention",
|
"intervention",
|
||||||
"is_pik",
|
|
||||||
"is_cef",
|
"is_cef",
|
||||||
"is_coherence_keeping",
|
"is_coherence_keeping",
|
||||||
"comment",
|
"comment",
|
||||||
@ -251,7 +234,6 @@ class NewCompensationForm(AbstractCompensationForm,
|
|||||||
intervention = self.cleaned_data.get("intervention", None)
|
intervention = self.cleaned_data.get("intervention", None)
|
||||||
is_cef = self.cleaned_data.get("is_cef", None)
|
is_cef = self.cleaned_data.get("is_cef", None)
|
||||||
is_coherence_keeping = self.cleaned_data.get("is_coherence_keeping", None)
|
is_coherence_keeping = self.cleaned_data.get("is_coherence_keeping", None)
|
||||||
is_pik = self.cleaned_data.get("is_pik", None)
|
|
||||||
comment = self.cleaned_data.get("comment", None)
|
comment = self.cleaned_data.get("comment", None)
|
||||||
|
|
||||||
# Create log entry
|
# Create log entry
|
||||||
@ -267,7 +249,6 @@ class NewCompensationForm(AbstractCompensationForm,
|
|||||||
created=action,
|
created=action,
|
||||||
is_cef=is_cef,
|
is_cef=is_cef,
|
||||||
is_coherence_keeping=is_coherence_keeping,
|
is_coherence_keeping=is_coherence_keeping,
|
||||||
is_pik=is_pik,
|
|
||||||
geometry=geometry,
|
geometry=geometry,
|
||||||
comment=comment,
|
comment=comment,
|
||||||
)
|
)
|
||||||
@ -300,7 +281,6 @@ class EditCompensationForm(NewCompensationForm):
|
|||||||
"intervention": self.instance.intervention,
|
"intervention": self.instance.intervention,
|
||||||
"is_cef": self.instance.is_cef,
|
"is_cef": self.instance.is_cef,
|
||||||
"is_coherence_keeping": self.instance.is_coherence_keeping,
|
"is_coherence_keeping": self.instance.is_coherence_keeping,
|
||||||
"is_pik": self.instance.is_pik,
|
|
||||||
"comment": self.instance.comment,
|
"comment": self.instance.comment,
|
||||||
}
|
}
|
||||||
disabled_fields = []
|
disabled_fields = []
|
||||||
@ -317,7 +297,6 @@ class EditCompensationForm(NewCompensationForm):
|
|||||||
intervention = self.cleaned_data.get("intervention", None)
|
intervention = self.cleaned_data.get("intervention", None)
|
||||||
is_cef = self.cleaned_data.get("is_cef", None)
|
is_cef = self.cleaned_data.get("is_cef", None)
|
||||||
is_coherence_keeping = self.cleaned_data.get("is_coherence_keeping", None)
|
is_coherence_keeping = self.cleaned_data.get("is_coherence_keeping", None)
|
||||||
is_pik = self.cleaned_data.get("is_pik", None)
|
|
||||||
comment = self.cleaned_data.get("comment", None)
|
comment = self.cleaned_data.get("comment", None)
|
||||||
|
|
||||||
# Create log entry
|
# Create log entry
|
||||||
@ -334,7 +313,6 @@ class EditCompensationForm(NewCompensationForm):
|
|||||||
self.instance.is_cef = is_cef
|
self.instance.is_cef = is_cef
|
||||||
self.instance.is_coherence_keeping = is_coherence_keeping
|
self.instance.is_coherence_keeping = is_coherence_keeping
|
||||||
self.instance.comment = comment
|
self.instance.comment = comment
|
||||||
self.instance.is_pik = is_pik
|
|
||||||
self.instance.modified = action
|
self.instance.modified = action
|
||||||
self.instance.save()
|
self.instance.save()
|
||||||
|
|
||||||
@ -344,7 +322,7 @@ class EditCompensationForm(NewCompensationForm):
|
|||||||
return self.instance
|
return self.instance
|
||||||
|
|
||||||
|
|
||||||
class NewEcoAccountForm(AbstractCompensationForm, CompensationResponsibleFormMixin, PikCompensationFormMixin):
|
class NewEcoAccountForm(AbstractCompensationForm, CompensationResponsibleFormMixin):
|
||||||
""" Form for creating eco accounts
|
""" Form for creating eco accounts
|
||||||
|
|
||||||
Inherits from basic AbstractCompensationForm and further form fields from CompensationResponsibleFormMixin
|
Inherits from basic AbstractCompensationForm and further form fields from CompensationResponsibleFormMixin
|
||||||
@ -385,7 +363,6 @@ class NewEcoAccountForm(AbstractCompensationForm, CompensationResponsibleFormMix
|
|||||||
"registration_date",
|
"registration_date",
|
||||||
"surface",
|
"surface",
|
||||||
"conservation_file_number",
|
"conservation_file_number",
|
||||||
"is_pik",
|
|
||||||
"handler_type",
|
"handler_type",
|
||||||
"handler_detail",
|
"handler_detail",
|
||||||
"comment",
|
"comment",
|
||||||
@ -415,7 +392,6 @@ class NewEcoAccountForm(AbstractCompensationForm, CompensationResponsibleFormMix
|
|||||||
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)
|
||||||
is_pik = self.cleaned_data.get("is_pik", None)
|
|
||||||
comment = self.cleaned_data.get("comment", None)
|
comment = self.cleaned_data.get("comment", None)
|
||||||
|
|
||||||
# Create log entry
|
# Create log entry
|
||||||
@ -447,7 +423,6 @@ class NewEcoAccountForm(AbstractCompensationForm, CompensationResponsibleFormMix
|
|||||||
created=action,
|
created=action,
|
||||||
geometry=geometry,
|
geometry=geometry,
|
||||||
comment=comment,
|
comment=comment,
|
||||||
is_pik=is_pik,
|
|
||||||
legal=legal
|
legal=legal
|
||||||
)
|
)
|
||||||
acc.share_with_user(user)
|
acc.share_with_user(user)
|
||||||
@ -483,7 +458,6 @@ class EditEcoAccountForm(NewEcoAccountForm):
|
|||||||
"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,
|
||||||
"is_pik": self.instance.is_pik,
|
|
||||||
"comment": self.instance.comment,
|
"comment": self.instance.comment,
|
||||||
}
|
}
|
||||||
disabled_fields = []
|
disabled_fields = []
|
||||||
@ -504,7 +478,6 @@ class EditEcoAccountForm(NewEcoAccountForm):
|
|||||||
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)
|
||||||
is_pik = self.cleaned_data.get("is_pik", None)
|
|
||||||
|
|
||||||
# Create log entry
|
# Create log entry
|
||||||
action = UserActionLogEntry.get_edited_action(user)
|
action = UserActionLogEntry.get_edited_action(user)
|
||||||
@ -530,7 +503,6 @@ class EditEcoAccountForm(NewEcoAccountForm):
|
|||||||
self.instance.deductable_surface = surface
|
self.instance.deductable_surface = surface
|
||||||
self.instance.geometry = geometry
|
self.instance.geometry = geometry
|
||||||
self.instance.comment = comment
|
self.instance.comment = comment
|
||||||
self.instance.is_pik = is_pik
|
|
||||||
self.instance.modified = action
|
self.instance.modified = action
|
||||||
self.instance.save()
|
self.instance.save()
|
||||||
|
|
||||||
|
@ -17,8 +17,7 @@ from codelist.models import KonovaCode
|
|||||||
from codelist.settings import CODELIST_BIOTOPES_ID, CODELIST_COMPENSATION_ACTION_ID, CODELIST_BIOTOPES_EXTRA_CODES_ID, \
|
from codelist.settings import CODELIST_BIOTOPES_ID, CODELIST_COMPENSATION_ACTION_ID, CODELIST_BIOTOPES_EXTRA_CODES_ID, \
|
||||||
CODELIST_COMPENSATION_ACTION_DETAIL_ID
|
CODELIST_COMPENSATION_ACTION_DETAIL_ID
|
||||||
from compensation.models import CompensationDocument, EcoAccountDocument
|
from compensation.models import CompensationDocument, EcoAccountDocument
|
||||||
from intervention.inputs import CompensationActionTreeCheckboxSelectMultiple, \
|
from intervention.inputs import CompensationActionTreeCheckboxSelectMultiple
|
||||||
CompensationStateTreeRadioSelect
|
|
||||||
from konova.contexts import BaseContext
|
from konova.contexts import BaseContext
|
||||||
from konova.forms import BaseModalForm, NewDocumentModalForm, RemoveModalForm
|
from konova.forms import BaseModalForm, NewDocumentModalForm, RemoveModalForm
|
||||||
from konova.models import DeadlineType
|
from konova.models import DeadlineType
|
||||||
@ -129,7 +128,6 @@ 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
|
||||||
|
|
||||||
|
|
||||||
@ -157,12 +155,22 @@ class NewStateModalForm(BaseModalForm):
|
|||||||
What has been on this area before changes/compensations have been applied and what will be the result ('after')?
|
What has been on this area before changes/compensations have been applied and what will be the result ('after')?
|
||||||
|
|
||||||
"""
|
"""
|
||||||
biotope_type = forms.ChoiceField(
|
biotope_type = forms.ModelChoiceField(
|
||||||
label=_("Biotope Type"),
|
label=_("Biotope Type"),
|
||||||
label_suffix="",
|
label_suffix="",
|
||||||
required=True,
|
required=True,
|
||||||
help_text=_("Select the biotope type"),
|
help_text=_("Select the biotope type"),
|
||||||
widget=CompensationStateTreeRadioSelect(),
|
queryset=KonovaCode.objects.filter(
|
||||||
|
is_archived=False,
|
||||||
|
is_leaf=True,
|
||||||
|
code_lists__in=[CODELIST_BIOTOPES_ID],
|
||||||
|
),
|
||||||
|
widget=autocomplete.ModelSelect2(
|
||||||
|
url="codes-biotope-autocomplete",
|
||||||
|
attrs={
|
||||||
|
"data-placeholder": _("Biotope Type"),
|
||||||
|
}
|
||||||
|
),
|
||||||
)
|
)
|
||||||
biotope_extra = forms.ModelMultipleChoiceField(
|
biotope_extra = forms.ModelMultipleChoiceField(
|
||||||
label=_("Biotope additional type"),
|
label=_("Biotope additional type"),
|
||||||
@ -200,16 +208,6 @@ class NewStateModalForm(BaseModalForm):
|
|||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.form_title = _("New state")
|
self.form_title = _("New state")
|
||||||
self.form_caption = _("Insert data for the new state")
|
self.form_caption = _("Insert data for the new state")
|
||||||
choices = KonovaCode.objects.filter(
|
|
||||||
code_lists__in=[CODELIST_BIOTOPES_ID],
|
|
||||||
is_archived=False,
|
|
||||||
is_leaf=True,
|
|
||||||
).values_list("id", flat=True)
|
|
||||||
choices = [
|
|
||||||
(choice, choice)
|
|
||||||
for choice in choices
|
|
||||||
]
|
|
||||||
self.fields["biotope_type"].choices = choices
|
|
||||||
|
|
||||||
def save(self, is_before_state: bool = False):
|
def save(self, is_before_state: bool = False):
|
||||||
state = self.instance.add_state(self, is_before_state)
|
state = self.instance.add_state(self, is_before_state)
|
||||||
@ -272,9 +270,8 @@ class EditCompensationStateModalForm(NewStateModalForm):
|
|||||||
self.state = kwargs.pop("state", None)
|
self.state = kwargs.pop("state", None)
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.form_title = _("Edit state")
|
self.form_title = _("Edit state")
|
||||||
biotope_type_id = self.state.biotope_type.id if self.state.biotope_type else None
|
|
||||||
form_data = {
|
form_data = {
|
||||||
"biotope_type": biotope_type_id,
|
"biotope_type": self.state.biotope_type,
|
||||||
"biotope_extra": self.state.biotope_type_details.all(),
|
"biotope_extra": self.state.biotope_type_details.all(),
|
||||||
"surface": self.state.surface,
|
"surface": self.state.surface,
|
||||||
}
|
}
|
||||||
@ -282,8 +279,7 @@ class EditCompensationStateModalForm(NewStateModalForm):
|
|||||||
|
|
||||||
def save(self, is_before_state: bool = False):
|
def save(self, is_before_state: bool = False):
|
||||||
state = self.state
|
state = self.state
|
||||||
biotope_type_id = self.cleaned_data.get("biotope_type", None)
|
state.biotope_type = self.cleaned_data.get("biotope_type", None)
|
||||||
state.biotope_type = KonovaCode.objects.get(id=biotope_type_id)
|
|
||||||
state.biotope_type_details.set(self.cleaned_data.get("biotope_extra", []))
|
state.biotope_type_details.set(self.cleaned_data.get("biotope_extra", []))
|
||||||
state.surface = self.cleaned_data.get("surface", None)
|
state.surface = self.cleaned_data.get("surface", None)
|
||||||
state.save()
|
state.save()
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
# Generated by Django 3.1.3 on 2022-05-31 10:45
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('compensation', '0006_ecoaccount_teams'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='compensation',
|
|
||||||
name='is_pik',
|
|
||||||
field=models.BooleanField(blank=True, default=False, help_text="Flag if compensation is a 'Produktonsintegrierte Kompensation'", null=True),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='ecoaccount',
|
|
||||||
name='is_pik',
|
|
||||||
field=models.BooleanField(blank=True, default=False, help_text="Flag if compensation is a 'Produktonsintegrierte Kompensation'", null=True),
|
|
||||||
),
|
|
||||||
]
|
|
@ -8,8 +8,6 @@ Created on: 16.11.21
|
|||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
|
|
||||||
from codelist.models import KonovaCode
|
|
||||||
from user.models import User, Team
|
from user.models import User, Team
|
||||||
from django.db import models, transaction
|
from django.db import models, transaction
|
||||||
from django.db.models import QuerySet, Sum
|
from django.db.models import QuerySet, Sum
|
||||||
@ -144,10 +142,8 @@ class AbstractCompensation(BaseObject, GeoReferencedMixin):
|
|||||||
"""
|
"""
|
||||||
form_data = form.cleaned_data
|
form_data = form.cleaned_data
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
biotope_type_id = form_data["biotope_type"]
|
|
||||||
code = KonovaCode.objects.get(id=biotope_type_id)
|
|
||||||
state = CompensationState.objects.create(
|
state = CompensationState.objects.create(
|
||||||
biotope_type=code,
|
biotope_type=form_data["biotope_type"],
|
||||||
surface=form_data["surface"],
|
surface=form_data["surface"],
|
||||||
)
|
)
|
||||||
state_additional_types = form_data["biotope_extra"]
|
state_additional_types = form_data["biotope_extra"]
|
||||||
@ -257,22 +253,7 @@ class CoherenceMixin(models.Model):
|
|||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
|
|
||||||
class PikMixin(models.Model):
|
class Compensation(AbstractCompensation, CEFMixin, CoherenceMixin):
|
||||||
""" Provides PIK flag as Mixin
|
|
||||||
|
|
||||||
"""
|
|
||||||
is_pik = models.BooleanField(
|
|
||||||
blank=True,
|
|
||||||
null=True,
|
|
||||||
default=False,
|
|
||||||
help_text="Flag if compensation is a 'Produktonsintegrierte Kompensation'"
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
|
|
||||||
class Compensation(AbstractCompensation, CEFMixin, CoherenceMixin, PikMixin):
|
|
||||||
"""
|
"""
|
||||||
Regular compensation, linked to an intervention
|
Regular compensation, linked to an intervention
|
||||||
"""
|
"""
|
||||||
@ -437,18 +418,6 @@ class Compensation(AbstractCompensation, CEFMixin, CoherenceMixin, PikMixin):
|
|||||||
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):
|
||||||
"""
|
"""
|
||||||
|
@ -17,13 +17,14 @@ from django.db.models import Sum, QuerySet
|
|||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from compensation.managers import EcoAccountManager, EcoAccountDeductionManager
|
from compensation.managers import EcoAccountManager, EcoAccountDeductionManager
|
||||||
from compensation.models.compensation import AbstractCompensation, PikMixin
|
from compensation.models.compensation import AbstractCompensation
|
||||||
from compensation.utils.quality import EcoAccountQualityChecker
|
from compensation.utils.quality import EcoAccountQualityChecker
|
||||||
from konova.models import ShareableObjectMixin, RecordableObjectMixin, AbstractDocument, BaseResource, \
|
from konova.models import ShareableObjectMixin, RecordableObjectMixin, AbstractDocument, BaseResource, \
|
||||||
generate_document_file_upload_path
|
generate_document_file_upload_path
|
||||||
|
from konova.settings import DEFAULT_SRID_RLP, LANIS_LINK_TEMPLATE
|
||||||
|
|
||||||
|
|
||||||
class EcoAccount(AbstractCompensation, ShareableObjectMixin, RecordableObjectMixin, PikMixin):
|
class EcoAccount(AbstractCompensation, ShareableObjectMixin, RecordableObjectMixin):
|
||||||
"""
|
"""
|
||||||
An eco account is a kind of 'prepaid' compensation. It can be compared to an account that already has been filled
|
An eco account is a kind of 'prepaid' compensation. It can be compared to an account that already has been filled
|
||||||
with some kind of currency. From this account one is able to deduct currency for current projects.
|
with some kind of currency. From this account one is able to deduct currency for current projects.
|
||||||
|
@ -5,15 +5,17 @@ Contact: michel.peltriaux@sgdnord.rlp.de
|
|||||||
Created on: 01.12.20
|
Created on: 01.12.20
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from konova.utils.message_templates import DATA_IS_UNCHECKED, DATA_CHECKED_ON_TEMPLATE, DATA_CHECKED_PREVIOUSLY_TEMPLATE
|
from user.models import User
|
||||||
from django.http import HttpRequest
|
from django.http import HttpRequest
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.html import format_html
|
from django.utils.html import format_html
|
||||||
|
from django.utils.timezone import localtime
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from compensation.filters import CompensationTableFilter, EcoAccountTableFilter
|
from compensation.filters import CompensationTableFilter, EcoAccountTableFilter
|
||||||
from compensation.models import Compensation, EcoAccount
|
from compensation.models import Compensation, EcoAccount
|
||||||
|
from konova.sub_settings.django_settings import DEFAULT_DATE_TIME_FORMAT
|
||||||
from konova.utils.tables import BaseTable, TableRenderMixin
|
from konova.utils.tables import BaseTable, TableRenderMixin
|
||||||
import django_tables2 as tables
|
import django_tables2 as tables
|
||||||
|
|
||||||
@ -109,21 +111,16 @@ class CompensationTable(BaseTable, TableRenderMixin):
|
|||||||
"""
|
"""
|
||||||
html = ""
|
html = ""
|
||||||
checked = value is not None
|
checked = value is not None
|
||||||
tooltip = DATA_IS_UNCHECKED
|
tooltip = _("Not checked yet")
|
||||||
previously_checked = record.intervention.get_last_checked_action()
|
|
||||||
if checked:
|
if checked:
|
||||||
checked_on = value.get_timestamp_str_formatted()
|
value = value.timestamp
|
||||||
tooltip = DATA_CHECKED_ON_TEMPLATE.format(checked_on, record.intervention.checked.user)
|
value = localtime(value)
|
||||||
|
checked_on = value.strftime(DEFAULT_DATE_TIME_FORMAT)
|
||||||
|
tooltip = _("Checked on {} by {}").format(checked_on, record.intervention.checked.user)
|
||||||
html += self.render_checked_star(
|
html += self.render_checked_star(
|
||||||
tooltip=tooltip,
|
tooltip=tooltip,
|
||||||
icn_filled=checked,
|
icn_filled=checked,
|
||||||
)
|
)
|
||||||
if previously_checked and not checked:
|
|
||||||
checked_on = previously_checked.get_timestamp_str_formatted()
|
|
||||||
tooltip = DATA_CHECKED_PREVIOUSLY_TEMPLATE.format(checked_on, previously_checked.user)
|
|
||||||
html += self.render_previously_checked_star(
|
|
||||||
tooltip=tooltip,
|
|
||||||
)
|
|
||||||
return format_html(html)
|
return format_html(html)
|
||||||
|
|
||||||
def render_d(self, value, record: Compensation):
|
def render_d(self, value, record: Compensation):
|
||||||
@ -137,7 +134,7 @@ class CompensationTable(BaseTable, TableRenderMixin):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
parcels = value.get_underlying_parcels().values_list(
|
parcels = value.get_underlying_parcels().values_list(
|
||||||
"parcel_group__name",
|
"gmrkng",
|
||||||
flat=True
|
flat=True
|
||||||
).distinct()
|
).distinct()
|
||||||
html = render_to_string(
|
html = render_to_string(
|
||||||
@ -162,7 +159,9 @@ class CompensationTable(BaseTable, TableRenderMixin):
|
|||||||
recorded = value is not None
|
recorded = value is not None
|
||||||
tooltip = _("Not recorded yet")
|
tooltip = _("Not recorded yet")
|
||||||
if recorded:
|
if recorded:
|
||||||
on = value.get_timestamp_str_formatted()
|
value = value.timestamp
|
||||||
|
value = localtime(value)
|
||||||
|
on = value.strftime(DEFAULT_DATE_TIME_FORMAT)
|
||||||
tooltip = _("Recorded on {} by {}").format(on, record.intervention.recorded.user)
|
tooltip = _("Recorded on {} by {}").format(on, record.intervention.recorded.user)
|
||||||
html += self.render_bookmark(
|
html += self.render_bookmark(
|
||||||
tooltip=tooltip,
|
tooltip=tooltip,
|
||||||
@ -180,7 +179,11 @@ class CompensationTable(BaseTable, TableRenderMixin):
|
|||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
has_access = record.is_shared_with(self.user)
|
if value is None:
|
||||||
|
value = User.objects.none()
|
||||||
|
has_access = value.filter(
|
||||||
|
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"),
|
||||||
@ -292,7 +295,7 @@ class EcoAccountTable(BaseTable, TableRenderMixin):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
parcels = value.get_underlying_parcels().values_list(
|
parcels = value.get_underlying_parcels().values_list(
|
||||||
"parcel_group__name",
|
"gmrkng",
|
||||||
flat=True
|
flat=True
|
||||||
).distinct()
|
).distinct()
|
||||||
html = render_to_string(
|
html = render_to_string(
|
||||||
@ -317,7 +320,9 @@ class EcoAccountTable(BaseTable, TableRenderMixin):
|
|||||||
checked = value is not None
|
checked = value is not None
|
||||||
tooltip = _("Not recorded yet. Can not be used for deductions, yet.")
|
tooltip = _("Not recorded yet. Can not be used for deductions, yet.")
|
||||||
if checked:
|
if checked:
|
||||||
on = value.get_timestamp_str_formatted()
|
value = value.timestamp
|
||||||
|
value = localtime(value)
|
||||||
|
on = value.strftime(DEFAULT_DATE_TIME_FORMAT)
|
||||||
tooltip = _("Recorded on {} by {}").format(on, record.recorded.user)
|
tooltip = _("Recorded on {} by {}").format(on, record.recorded.user)
|
||||||
html += self.render_bookmark(
|
html += self.render_bookmark(
|
||||||
tooltip=tooltip,
|
tooltip=tooltip,
|
||||||
@ -338,7 +343,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 = record.is_shared_with(self.user)
|
has_access = self.user in record.users.all()
|
||||||
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",
|
||||||
|
@ -39,16 +39,6 @@
|
|||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<th scope="row">{% trans 'Is PIK' %}</th>
|
|
||||||
<td class="align-middle">
|
|
||||||
{% if obj.is_pik %}
|
|
||||||
{% trans 'Yes' %}
|
|
||||||
{% else %}
|
|
||||||
{% trans 'No' %}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans 'Is CEF compensation' %}</th>
|
<th scope="row">{% trans 'Is CEF compensation' %}</th>
|
||||||
<td class="align-middle">
|
<td class="align-middle">
|
||||||
@ -76,11 +66,6 @@
|
|||||||
<span>
|
<span>
|
||||||
{% fa5_icon 'star' 'far' %}
|
{% fa5_icon 'star' 'far' %}
|
||||||
</span>
|
</span>
|
||||||
{% if last_checked %}
|
|
||||||
<span class="rlp-gd-inv" title="{{last_checked_tooltip}}">
|
|
||||||
{% fa5_icon 'star' 'fas' %}
|
|
||||||
</span>
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="check-star" title="{% trans 'Checked on '%} {{obj.intervention.checked.timestamp}} {% trans 'by' %} {{obj.intervention.checked.user}}">
|
<span class="check-star" title="{% trans 'Checked on '%} {{obj.intervention.checked.timestamp}} {% trans 'by' %} {{obj.intervention.checked.user}}">
|
||||||
{% fa5_icon 'star' %}
|
{% fa5_icon 'star' %}
|
||||||
@ -105,21 +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">
|
||||||
{% 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 %}
|
|
||||||
{{obj.created.timestamp|default_if_none:""}}
|
|
||||||
<br>
|
|
||||||
{{obj.created.user.username}}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans 'Shared with' %}</th>
|
<th scope="row">{% trans 'Shared with' %}</th>
|
||||||
<td class="align-middle">
|
<td class="align-middle">
|
||||||
{% for team in obj.intervention.shared_teams %}
|
{% for team in obj.intervention.teams.all %}
|
||||||
{% include 'user/includes/team_data_modal_button.html' %}
|
{% include 'user/includes/team_data_modal_button.html' %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<hr>
|
<hr>
|
||||||
@ -134,12 +113,10 @@
|
|||||||
<div class="col-sm-12 col-md-12 col-lg-12 col-xl-6">
|
<div class="col-sm-12 col-md-12 col-lg-12 col-xl-6">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12">
|
{% include 'map/geom_form.html' %}
|
||||||
{% include 'map/geom_form.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% include 'konova/includes/parcels/parcels.html' %}
|
{% include 'konova/includes/parcels.html' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% include 'konova/includes/comment_card.html' %}
|
{% include 'konova/includes/comment_card.html' %}
|
||||||
|
@ -70,34 +70,18 @@
|
|||||||
<th scope="row">{% trans 'Action handler' %}</th>
|
<th scope="row">{% trans 'Action handler' %}</th>
|
||||||
<td class="align-middle">{{obj.responsible.handler|default_if_none:""}}</td>
|
<td class="align-middle">{{obj.responsible.handler|default_if_none:""}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<th scope="row">{% trans 'Is PIK' %}</th>
|
|
||||||
<td class="align-middle">
|
|
||||||
{% if obj.is_pik %}
|
|
||||||
{% trans 'Yes' %}
|
|
||||||
{% else %}
|
|
||||||
{% trans 'No' %}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
<tr>
|
||||||
<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 %}
|
{{obj.modified.timestamp|default_if_none:""|naturalday}}
|
||||||
{{obj.modified.timestamp|default_if_none:""}}
|
<br>
|
||||||
<br>
|
{{obj.modified.user.username}}
|
||||||
{{obj.modified.user.username}}
|
|
||||||
{% else %}
|
|
||||||
{{obj.created.timestamp|default_if_none:""}}
|
|
||||||
<br>
|
|
||||||
{{obj.created.user.username}}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans 'Shared with' %}</th>
|
<th scope="row">{% trans 'Shared with' %}</th>
|
||||||
<td class="align-middle">
|
<td class="align-middle">
|
||||||
{% for team in obj.shared_teams %}
|
{% for team in obj.teams.all %}
|
||||||
{% include 'user/includes/team_data_modal_button.html' %}
|
{% include 'user/includes/team_data_modal_button.html' %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<hr>
|
<hr>
|
||||||
@ -111,12 +95,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-sm-12 col-md-12 col-lg-12 col-xl-6">
|
<div class="col-sm-12 col-md-12 col-lg-12 col-xl-6">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12">
|
{% include 'map/geom_form.html' %}
|
||||||
{% include 'map/geom_form.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% include 'konova/includes/parcels/parcels.html' %}
|
{% include 'konova/includes/parcels.html' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% include 'konova/includes/comment_card.html' %}
|
{% include 'konova/includes/comment_card.html' %}
|
||||||
|
@ -20,36 +20,6 @@
|
|||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<th scope="row">{% trans 'Is PIK' %}</th>
|
|
||||||
<td class="align-middle">
|
|
||||||
{% if obj.is_pik %}
|
|
||||||
{% trans 'Yes' %}
|
|
||||||
{% else %}
|
|
||||||
{% trans 'No' %}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row">{% trans 'Is CEF' %}</th>
|
|
||||||
<td class="align-middle">
|
|
||||||
{% if obj.is_cef %}
|
|
||||||
{% trans 'Yes' %}
|
|
||||||
{% else %}
|
|
||||||
{% trans 'No' %}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row">{% trans 'Is coherence keeping' %}</th>
|
|
||||||
<td class="align-middle">
|
|
||||||
{% if obj.is_coherence_keeping %}
|
|
||||||
{% trans 'Yes' %}
|
|
||||||
{% else %}
|
|
||||||
{% trans 'No' %}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans 'Last modified' %}</th>
|
<th scope="row">{% trans 'Last modified' %}</th>
|
||||||
<td class="align-middle">
|
<td class="align-middle">
|
||||||
@ -65,15 +35,20 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-sm-12 col-md-12 col-lg-12 col-xl-6">
|
<div class="col-sm-12 col-md-12 col-lg-12 col-xl-6">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12">
|
{% include 'map/geom_form.html' %}
|
||||||
{% include 'map/geom_form.html' %}
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
{% include 'konova/includes/parcels.html' %}
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6 col-md-6 col-lg-6">
|
||||||
|
<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 class="row">
|
|
||||||
{% include 'konova/includes/parcels/parcels.html' %}
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
{% include 'konova/includes/report/qrcodes.html' %}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -20,16 +20,6 @@
|
|||||||
<th scope="row">{% trans 'Conservation office file number' %}</th>
|
<th scope="row">{% trans 'Conservation office file number' %}</th>
|
||||||
<td class="align-middle">{{obj.responsible.conservation_file_number|default_if_none:""}}</td>
|
<td class="align-middle">{{obj.responsible.conservation_file_number|default_if_none:""}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<th scope="row">{% trans 'Is PIK' %}</th>
|
|
||||||
<td class="align-middle">
|
|
||||||
{% if obj.is_pik %}
|
|
||||||
{% trans 'Yes' %}
|
|
||||||
{% else %}
|
|
||||||
{% trans 'No' %}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans 'Deductions for' %}</th>
|
<th scope="row">{% trans 'Deductions for' %}</th>
|
||||||
<td class="align-middle">
|
<td class="align-middle">
|
||||||
@ -58,15 +48,20 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-sm-12 col-md-12 col-lg-12 col-xl-6">
|
<div class="col-sm-12 col-md-12 col-lg-12 col-xl-6">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12">
|
{% include 'map/geom_form.html' %}
|
||||||
{% include 'map/geom_form.html' %}
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
{% include 'konova/includes/parcels.html' %}
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6 col-md-6 col-lg-6">
|
||||||
|
<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 class="row">
|
|
||||||
{% include 'konova/includes/parcels/parcels.html' %}
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
{% include 'konova/includes/report/qrcodes.html' %}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -50,20 +50,18 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase):
|
|||||||
test_id = self.create_dummy_string()
|
test_id = self.create_dummy_string()
|
||||||
test_title = self.create_dummy_string()
|
test_title = self.create_dummy_string()
|
||||||
test_geom = self.create_dummy_geometry()
|
test_geom = self.create_dummy_geometry()
|
||||||
geom_json = self.create_geojson(test_geom)
|
|
||||||
post_data = {
|
post_data = {
|
||||||
"identifier": test_id,
|
"identifier": test_id,
|
||||||
"title": test_title,
|
"title": test_title,
|
||||||
"geom": geom_json,
|
"geom": test_geom.geojson,
|
||||||
"intervention": self.intervention.id,
|
"intervention": self.intervention.id,
|
||||||
}
|
}
|
||||||
pre_creation_intervention_log_count = self.intervention.log.count()
|
pre_creation_intervention_log_count = self.intervention.log.count()
|
||||||
|
|
||||||
# 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()
|
||||||
response = self.client_user.post(new_url, post_data)
|
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)
|
||||||
@ -89,11 +87,10 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase):
|
|||||||
test_id = self.create_dummy_string()
|
test_id = self.create_dummy_string()
|
||||||
test_title = self.create_dummy_string()
|
test_title = self.create_dummy_string()
|
||||||
test_geom = self.create_dummy_geometry()
|
test_geom = self.create_dummy_geometry()
|
||||||
geom_json = self.create_geojson(test_geom)
|
|
||||||
post_data = {
|
post_data = {
|
||||||
"identifier": test_id,
|
"identifier": test_id,
|
||||||
"title": test_title,
|
"title": test_title,
|
||||||
"geom": geom_json,
|
"geom": test_geom.geojson,
|
||||||
}
|
}
|
||||||
pre_creation_intervention_log_count = self.intervention.log.count()
|
pre_creation_intervention_log_count = self.intervention.log.count()
|
||||||
|
|
||||||
@ -128,7 +125,6 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase):
|
|||||||
new_identifier = self.create_dummy_string()
|
new_identifier = self.create_dummy_string()
|
||||||
new_comment = self.create_dummy_string()
|
new_comment = self.create_dummy_string()
|
||||||
new_geometry = MultiPolygon(srid=4326) # Create an empty geometry
|
new_geometry = MultiPolygon(srid=4326) # Create an empty geometry
|
||||||
geojson = self.create_geojson(new_geometry)
|
|
||||||
|
|
||||||
check_on_elements = {
|
check_on_elements = {
|
||||||
self.compensation.title: new_title,
|
self.compensation.title: new_title,
|
||||||
@ -143,7 +139,7 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase):
|
|||||||
"title": new_title,
|
"title": new_title,
|
||||||
"intervention": self.intervention.id, # just keep the intervention as it is
|
"intervention": self.intervention.id, # just keep the intervention as it is
|
||||||
"comment": new_comment,
|
"comment": new_comment,
|
||||||
"geom": geojson,
|
"geom": new_geometry.geojson,
|
||||||
}
|
}
|
||||||
self.client_user.post(url, post_data)
|
self.client_user.post(url, post_data)
|
||||||
self.compensation.refresh_from_db()
|
self.compensation.refresh_from_db()
|
||||||
@ -265,26 +261,3 @@ 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)
|
|
||||||
|
@ -40,13 +40,12 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase):
|
|||||||
test_id = self.create_dummy_string()
|
test_id = self.create_dummy_string()
|
||||||
test_title = self.create_dummy_string()
|
test_title = self.create_dummy_string()
|
||||||
test_geom = self.create_dummy_geometry()
|
test_geom = self.create_dummy_geometry()
|
||||||
geom_json = self.create_geojson(test_geom)
|
|
||||||
test_deductable_surface = 1000
|
test_deductable_surface = 1000
|
||||||
test_conservation_office = self.get_conservation_office_code()
|
test_conservation_office = self.get_conservation_office_code()
|
||||||
post_data = {
|
post_data = {
|
||||||
"identifier": test_id,
|
"identifier": test_id,
|
||||||
"title": test_title,
|
"title": test_title,
|
||||||
"geom": geom_json,
|
"geom": test_geom.geojson,
|
||||||
"deductable_surface": test_deductable_surface,
|
"deductable_surface": test_deductable_surface,
|
||||||
"conservation_office": test_conservation_office.id
|
"conservation_office": test_conservation_office.id
|
||||||
}
|
}
|
||||||
@ -303,27 +302,3 @@ 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,5 +1,4 @@
|
|||||||
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
|
||||||
@ -23,7 +22,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, RECORDED_BLOCKS_EDIT, PARAMS_INVALID, DATA_CHECKED_PREVIOUSLY_TEMPLATE
|
DEADLINE_EDITED
|
||||||
from konova.utils.user_checks import in_group
|
from konova.utils.user_checks import in_group
|
||||||
|
|
||||||
|
|
||||||
@ -70,19 +69,6 @@ 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":
|
||||||
@ -148,13 +134,6 @@ 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)
|
||||||
@ -217,15 +196,8 @@ def detail_view(request: HttpRequest, id: str):
|
|||||||
|
|
||||||
request = comp.set_status_messages(request)
|
request = comp.set_status_messages(request)
|
||||||
|
|
||||||
last_checked = comp.intervention.get_last_checked_action()
|
|
||||||
last_checked_tooltip = ""
|
|
||||||
if last_checked:
|
|
||||||
last_checked_tooltip = DATA_CHECKED_PREVIOUSLY_TEMPLATE.format(last_checked.get_timestamp_str_formatted(), last_checked.user)
|
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
"obj": comp,
|
"obj": comp,
|
||||||
"last_checked": last_checked,
|
|
||||||
"last_checked_tooltip": last_checked_tooltip,
|
|
||||||
"geom_form": geom_form,
|
"geom_form": geom_form,
|
||||||
"parcels": parcels,
|
"parcels": parcels,
|
||||||
"has_access": is_data_shared,
|
"has_access": is_data_shared,
|
||||||
@ -624,12 +596,14 @@ 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(
|
||||||
qrcode_url = request.build_absolute_uri(reverse("compensation:report", args=(id,)))
|
request.build_absolute_uri(reverse("compensation:report", args=(id,))),
|
||||||
qrcode_img = generate_qr_code(qrcode_url, 10)
|
10
|
||||||
qrcode_lanis_url = comp.get_LANIS_link()
|
)
|
||||||
qrcode_img_lanis = generate_qr_code(qrcode_lanis_url, 7)
|
qrcode_img_lanis = generate_qr_code(
|
||||||
|
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")
|
||||||
@ -637,14 +611,8 @@ def report_view(request: HttpRequest, id: str):
|
|||||||
|
|
||||||
context = {
|
context = {
|
||||||
"obj": comp,
|
"obj": comp,
|
||||||
"qrcode": {
|
"qrcode": qrcode_img,
|
||||||
"img": qrcode_img,
|
"qrcode_lanis": qrcode_img_lanis,
|
||||||
"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,8 +35,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, \
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
@ -146,13 +145,6 @@ 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)
|
||||||
@ -739,16 +731,18 @@ 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(
|
||||||
qrcode_url = request.build_absolute_uri(reverse("ema:report", args=(id,)))
|
request.build_absolute_uri(reverse("ema:report", args=(id,))),
|
||||||
qrcode_img = generate_qr_code(qrcode_url, 10)
|
10
|
||||||
qrcode_lanis_url = acc.get_LANIS_link()
|
)
|
||||||
qrcode_img_lanis = generate_qr_code(qrcode_lanis_url, 7)
|
qrcode_img_lanis = generate_qr_code(
|
||||||
|
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().prefetch_related("action_type__parent")
|
actions = acc.actions.all().select_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()\
|
||||||
@ -758,14 +752,8 @@ def report_view(request:HttpRequest, id: str):
|
|||||||
|
|
||||||
context = {
|
context = {
|
||||||
"obj": acc,
|
"obj": acc,
|
||||||
"qrcode": {
|
"qrcode": qrcode_img,
|
||||||
"img": qrcode_img,
|
"qrcode_lanis": qrcode_img_lanis,
|
||||||
"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,
|
||||||
|
11
ema/forms.py
11
ema/forms.py
@ -12,15 +12,14 @@ from django.db import transaction
|
|||||||
from django.urls import reverse, reverse_lazy
|
from django.urls import reverse, reverse_lazy
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from compensation.forms.forms import AbstractCompensationForm, CompensationResponsibleFormMixin, \
|
from compensation.forms.forms import AbstractCompensationForm, CompensationResponsibleFormMixin
|
||||||
PikCompensationFormMixin
|
|
||||||
from ema.models import Ema, EmaDocument
|
from ema.models import Ema, EmaDocument
|
||||||
from intervention.models import Responsibility, Handler
|
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
|
||||||
|
|
||||||
|
|
||||||
class NewEmaForm(AbstractCompensationForm, CompensationResponsibleFormMixin, PikCompensationFormMixin):
|
class NewEmaForm(AbstractCompensationForm, CompensationResponsibleFormMixin):
|
||||||
""" Form for creating new EMA objects.
|
""" Form for creating new EMA objects.
|
||||||
|
|
||||||
Inherits basic form fields from AbstractCompensationForm and additional from CompensationResponsibleFormMixin.
|
Inherits basic form fields from AbstractCompensationForm and additional from CompensationResponsibleFormMixin.
|
||||||
@ -32,7 +31,6 @@ class NewEmaForm(AbstractCompensationForm, CompensationResponsibleFormMixin, Pik
|
|||||||
"title",
|
"title",
|
||||||
"conservation_office",
|
"conservation_office",
|
||||||
"conservation_file_number",
|
"conservation_file_number",
|
||||||
"is_pik",
|
|
||||||
"handler_type",
|
"handler_type",
|
||||||
"handler_detail",
|
"handler_detail",
|
||||||
"comment",
|
"comment",
|
||||||
@ -60,7 +58,6 @@ class NewEmaForm(AbstractCompensationForm, CompensationResponsibleFormMixin, Pik
|
|||||||
handler_detail = self.cleaned_data.get("handler_detail", 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)
|
||||||
is_pik = self.cleaned_data.get("is_pik", None)
|
|
||||||
comment = self.cleaned_data.get("comment", None)
|
comment = self.cleaned_data.get("comment", None)
|
||||||
|
|
||||||
# Create log entry
|
# Create log entry
|
||||||
@ -86,7 +83,6 @@ class NewEmaForm(AbstractCompensationForm, CompensationResponsibleFormMixin, Pik
|
|||||||
created=action,
|
created=action,
|
||||||
geometry=geometry,
|
geometry=geometry,
|
||||||
comment=comment,
|
comment=comment,
|
||||||
is_pik=is_pik,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add the creating user to the list of shared users
|
# Add the creating user to the list of shared users
|
||||||
@ -120,7 +116,6 @@ class EditEmaForm(NewEmaForm):
|
|||||||
"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,
|
||||||
"is_pik": self.instance.is_pik,
|
|
||||||
}
|
}
|
||||||
disabled_fields = []
|
disabled_fields = []
|
||||||
self.load_initial_data(
|
self.load_initial_data(
|
||||||
@ -138,7 +133,6 @@ class EditEmaForm(NewEmaForm):
|
|||||||
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)
|
||||||
is_pik = self.cleaned_data.get("is_pik", None)
|
|
||||||
|
|
||||||
# Create log entry
|
# Create log entry
|
||||||
action = UserActionLogEntry.get_edited_action(user)
|
action = UserActionLogEntry.get_edited_action(user)
|
||||||
@ -158,7 +152,6 @@ class EditEmaForm(NewEmaForm):
|
|||||||
self.instance.title = title
|
self.instance.title = title
|
||||||
self.instance.geometry = geometry
|
self.instance.geometry = geometry
|
||||||
self.instance.comment = comment
|
self.instance.comment = comment
|
||||||
self.instance.is_pik = is_pik
|
|
||||||
self.instance.modified = action
|
self.instance.modified = action
|
||||||
self.instance.save()
|
self.instance.save()
|
||||||
|
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 3.1.3 on 2022-05-31 10:45
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('ema', '0003_ema_teams'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='ema',
|
|
||||||
name='is_pik',
|
|
||||||
field=models.BooleanField(blank=True, default=False, help_text="Flag if compensation is a 'Produktonsintegrierte Kompensation'", null=True),
|
|
||||||
),
|
|
||||||
]
|
|
@ -13,14 +13,15 @@ from django.db.models import QuerySet
|
|||||||
from django.http import HttpRequest
|
from django.http import HttpRequest
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
from compensation.models import AbstractCompensation, PikMixin
|
from compensation.models import AbstractCompensation
|
||||||
from ema.managers import EmaManager
|
from ema.managers import EmaManager
|
||||||
from ema.utils.quality import EmaQualityChecker
|
from ema.utils.quality import EmaQualityChecker
|
||||||
from konova.models import AbstractDocument, generate_document_file_upload_path, RecordableObjectMixin, ShareableObjectMixin
|
from konova.models import AbstractDocument, generate_document_file_upload_path, RecordableObjectMixin, ShareableObjectMixin
|
||||||
|
from konova.settings import DEFAULT_SRID_RLP, LANIS_LINK_TEMPLATE
|
||||||
from konova.utils.message_templates import DATA_UNSHARED_EXPLANATION, DOCUMENT_REMOVED_TEMPLATE
|
from konova.utils.message_templates import DATA_UNSHARED_EXPLANATION, DOCUMENT_REMOVED_TEMPLATE
|
||||||
|
|
||||||
|
|
||||||
class Ema(AbstractCompensation, ShareableObjectMixin, RecordableObjectMixin, PikMixin):
|
class Ema(AbstractCompensation, ShareableObjectMixin, RecordableObjectMixin):
|
||||||
"""
|
"""
|
||||||
EMA = Ersatzzahlungsmaßnahme
|
EMA = Ersatzzahlungsmaßnahme
|
||||||
(compensation actions from payments)
|
(compensation actions from payments)
|
||||||
|
@ -104,7 +104,7 @@ class EmaTable(BaseTable, TableRenderMixin):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
parcels = value.get_underlying_parcels().values_list(
|
parcels = value.get_underlying_parcels().values_list(
|
||||||
"parcel_group__name",
|
"gmrkng",
|
||||||
flat=True
|
flat=True
|
||||||
).distinct()
|
).distinct()
|
||||||
html = render_to_string(
|
html = render_to_string(
|
||||||
@ -115,6 +115,7 @@ class EmaTable(BaseTable, TableRenderMixin):
|
|||||||
)
|
)
|
||||||
return html
|
return html
|
||||||
|
|
||||||
|
|
||||||
def render_r(self, value, record: Ema):
|
def render_r(self, value, record: Ema):
|
||||||
""" Renders the registered column for a EMA
|
""" Renders the registered column for a EMA
|
||||||
|
|
||||||
@ -129,7 +130,9 @@ class EmaTable(BaseTable, TableRenderMixin):
|
|||||||
recorded = value is not None
|
recorded = value is not None
|
||||||
tooltip = _("Not recorded yet")
|
tooltip = _("Not recorded yet")
|
||||||
if recorded:
|
if recorded:
|
||||||
on = value.get_timestamp_str_formatted()
|
value = value.timestamp
|
||||||
|
value = localtime(value)
|
||||||
|
on = value.strftime(DEFAULT_DATE_TIME_FORMAT)
|
||||||
tooltip = _("Recorded on {} by {}").format(on, record.recorded.user)
|
tooltip = _("Recorded on {} by {}").format(on, record.recorded.user)
|
||||||
html += self.render_bookmark(
|
html += self.render_bookmark(
|
||||||
tooltip=tooltip,
|
tooltip=tooltip,
|
||||||
@ -148,7 +151,9 @@ class EmaTable(BaseTable, TableRenderMixin):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
html = ""
|
html = ""
|
||||||
has_access = record.is_shared_with(self.user)
|
has_access = value.filter(
|
||||||
|
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"),
|
||||||
|
@ -56,38 +56,29 @@
|
|||||||
<th scope="row">{% trans 'Action handler' %}</th>
|
<th scope="row">{% trans 'Action handler' %}</th>
|
||||||
<td class="align-middle">{{obj.responsible.handler|default_if_none:""}}</td>
|
<td class="align-middle">{{obj.responsible.handler|default_if_none:""}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<th scope="row">{% trans 'Is PIK' %}</th>
|
|
||||||
<td class="align-middle">
|
|
||||||
{% if obj.is_pik %}
|
|
||||||
{% trans 'Yes' %}
|
|
||||||
{% else %}
|
|
||||||
{% trans 'No' %}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
<tr>
|
||||||
<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:""}}
|
{{obj.modified.timestamp|default_if_none:""|naturalday}}
|
||||||
<br>
|
<br>
|
||||||
{{obj.modified.user.username}}
|
{{obj.modified.user.username}}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{obj.created.timestamp|default_if_none:""}}
|
{{obj.created.timestamp|default_if_none:""|naturalday}}
|
||||||
<br>
|
<br>
|
||||||
{{obj.created.user.username}}
|
{{obj.created.user.username}}
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans 'Shared with' %}</th>
|
<th scope="row">{% trans 'Shared with' %}</th>
|
||||||
<td class="align-middle">
|
<td class="align-middle">
|
||||||
{% for team in obj.shared_teams %}
|
{% for team in obj.teams.all %}
|
||||||
{% include 'user/includes/team_data_modal_button.html' %}
|
{% include 'user/includes/team_data_modal_button.html' %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<hr>
|
<hr>
|
||||||
{% for user in obj.user.all %}
|
{% for user in obj.users.all %}
|
||||||
{% include 'user/includes/contact_modal_button.html' %}
|
{% include 'user/includes/contact_modal_button.html' %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</td>
|
</td>
|
||||||
@ -97,12 +88,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-sm-12 col-md-12 col-lg-12 col-xl-6">
|
<div class="col-sm-12 col-md-12 col-lg-12 col-xl-6">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12">
|
{% include 'map/geom_form.html' %}
|
||||||
{% include 'map/geom_form.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% include 'konova/includes/parcels/parcels.html' %}
|
{% include 'konova/includes/parcels.html' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% include 'konova/includes/comment_card.html' %}
|
{% include 'konova/includes/comment_card.html' %}
|
||||||
|
@ -20,16 +20,6 @@
|
|||||||
<th scope="row">{% trans 'Conservation office file number' %}</th>
|
<th scope="row">{% trans 'Conservation office file number' %}</th>
|
||||||
<td class="align-middle">{{obj.responsible.conservation_file_number|default_if_none:""}}</td>
|
<td class="align-middle">{{obj.responsible.conservation_file_number|default_if_none:""}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<th scope="row">{% trans 'Is PIK' %}</th>
|
|
||||||
<td class="align-middle">
|
|
||||||
{% if obj.is_pik %}
|
|
||||||
{% trans 'Yes' %}
|
|
||||||
{% else %}
|
|
||||||
{% trans 'No' %}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans 'Last modified' %}</th>
|
<th scope="row">{% trans 'Last modified' %}</th>
|
||||||
<td class="align-middle">
|
<td class="align-middle">
|
||||||
@ -45,15 +35,20 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-sm-12 col-md-12 col-lg-12 col-xl-6">
|
<div class="col-sm-12 col-md-12 col-lg-12 col-xl-6">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12">
|
{% include 'map/geom_form.html' %}
|
||||||
{% include 'map/geom_form.html' %}
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
{% include 'konova/includes/parcels.html' %}
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6 col-md-6 col-lg-6">
|
||||||
|
<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 class="row">
|
|
||||||
{% include 'konova/includes/parcels/parcels.html' %}
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
{% include 'konova/includes/report/qrcodes.html' %}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -41,12 +41,11 @@ class EmaWorkflowTestCase(BaseWorkflowTestCase):
|
|||||||
test_id = self.create_dummy_string()
|
test_id = self.create_dummy_string()
|
||||||
test_title = self.create_dummy_string()
|
test_title = self.create_dummy_string()
|
||||||
test_geom = self.create_dummy_geometry()
|
test_geom = self.create_dummy_geometry()
|
||||||
geom_json = self.create_geojson(test_geom)
|
|
||||||
test_conservation_office = self.get_conservation_office_code()
|
test_conservation_office = self.get_conservation_office_code()
|
||||||
post_data = {
|
post_data = {
|
||||||
"identifier": test_id,
|
"identifier": test_id,
|
||||||
"title": test_title,
|
"title": test_title,
|
||||||
"geom": geom_json,
|
"geom": test_geom.geojson,
|
||||||
"conservation_office": test_conservation_office.id
|
"conservation_office": test_conservation_office.id
|
||||||
}
|
}
|
||||||
self.client_user.post(new_url, post_data)
|
self.client_user.post(new_url, post_data)
|
||||||
@ -118,32 +117,6 @@ 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, RECORDED_BLOCKS_EDIT
|
COMPENSATION_ACTION_EDITED, DEADLINE_EDITED
|
||||||
from konova.utils.user_checks import in_group
|
from konova.utils.user_checks import in_group
|
||||||
|
|
||||||
|
|
||||||
@ -213,13 +213,6 @@ 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)
|
||||||
@ -570,12 +563,14 @@ 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(
|
||||||
qrcode_url = request.build_absolute_uri(reverse("ema:report", args=(id,)))
|
request.build_absolute_uri(reverse("ema:report", args=(id,))),
|
||||||
qrcode_img = generate_qr_code(qrcode_url, 10)
|
10
|
||||||
qrcode_lanis_url = ema.get_LANIS_link()
|
)
|
||||||
qrcode_img_lanis = generate_qr_code(qrcode_lanis_url, 7)
|
qrcode_img_lanis = generate_qr_code(
|
||||||
|
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")
|
||||||
@ -583,14 +578,8 @@ def report_view(request:HttpRequest, id: str):
|
|||||||
|
|
||||||
context = {
|
context = {
|
||||||
"obj": ema,
|
"obj": ema,
|
||||||
"qrcode": {
|
"qrcode": qrcode_img,
|
||||||
"img": qrcode_img,
|
"qrcode_lanis": qrcode_img_lanis,
|
||||||
"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,14 +25,12 @@ 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",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -216,10 +216,6 @@ class NewInterventionForm(BaseForm):
|
|||||||
identifier = tmp_intervention.generate_new_identifier()
|
identifier = tmp_intervention.generate_new_identifier()
|
||||||
self.initialize_form_field("identifier", identifier)
|
self.initialize_form_field("identifier", identifier)
|
||||||
|
|
||||||
def is_valid(self):
|
|
||||||
super_valid_result = super().is_valid()
|
|
||||||
return super_valid_result
|
|
||||||
|
|
||||||
def save(self, user: User, geom_form: SimpleGeomForm):
|
def save(self, user: User, geom_form: SimpleGeomForm):
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
# Fetch data from cleaned POST values
|
# Fetch data from cleaned POST values
|
||||||
|
@ -427,22 +427,13 @@ 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)
|
||||||
)
|
)
|
||||||
objects_valid = False
|
return 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"])
|
||||||
@ -456,7 +447,7 @@ class NewDeductionModalForm(BaseModalForm):
|
|||||||
format_german_float(rest_surface),
|
format_german_float(rest_surface),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
return is_valid_surface and objects_valid and super_result
|
return is_valid_surface and super_result
|
||||||
|
|
||||||
def __create_deduction(self):
|
def __create_deduction(self):
|
||||||
""" Creates the deduction
|
""" Creates the deduction
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
from codelist.models import KonovaCode
|
from codelist.models import KonovaCode
|
||||||
from codelist.settings import CODELIST_COMPENSATION_ACTION_ID, CODELIST_BIOTOPES_ID
|
from codelist.settings import CODELIST_COMPENSATION_ACTION_ID
|
||||||
|
|
||||||
|
|
||||||
class DummyFilterInput(forms.HiddenInput):
|
class DummyFilterInput(forms.HiddenInput):
|
||||||
@ -38,17 +38,7 @@ class TreeCheckboxSelectMultiple(forms.CheckboxSelectMultiple):
|
|||||||
""" Provides multiple selection of parent-child data
|
""" Provides multiple selection of parent-child data
|
||||||
|
|
||||||
"""
|
"""
|
||||||
template_name = "konova/widgets/tree/checkbox/checkbox-tree-select.html"
|
template_name = "konova/widgets/checkbox-tree-select.html"
|
||||||
|
|
||||||
class meta:
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
|
|
||||||
class TreeRadioSelect(forms.RadioSelect):
|
|
||||||
""" Provides single selection of parent-child data
|
|
||||||
|
|
||||||
"""
|
|
||||||
template_name = "konova/widgets/tree/radio/radio-tree-select.html"
|
|
||||||
|
|
||||||
class meta:
|
class meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
@ -78,30 +68,6 @@ class KonovaCodeTreeCheckboxSelectMultiple(TreeCheckboxSelectMultiple):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class KonovaCodeTreeRadioSelect(TreeRadioSelect):
|
|
||||||
""" Provides single selection of KonovaCode
|
|
||||||
|
|
||||||
"""
|
|
||||||
filter = None
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.code_list = kwargs.pop("code_list", None)
|
|
||||||
self.filter = kwargs.pop("filter", {})
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def get_context(self, name, value, attrs):
|
|
||||||
context = super().get_context(name, value, attrs)
|
|
||||||
codes = KonovaCode.objects.filter(
|
|
||||||
**self.filter,
|
|
||||||
)
|
|
||||||
codes = [
|
|
||||||
parent_code.add_children()
|
|
||||||
for parent_code in codes
|
|
||||||
]
|
|
||||||
context["codes"] = codes
|
|
||||||
return context
|
|
||||||
|
|
||||||
|
|
||||||
class CompensationActionTreeCheckboxSelectMultiple(KonovaCodeTreeCheckboxSelectMultiple):
|
class CompensationActionTreeCheckboxSelectMultiple(KonovaCodeTreeCheckboxSelectMultiple):
|
||||||
""" Provides multiple selection of CompensationActions
|
""" Provides multiple selection of CompensationActions
|
||||||
|
|
||||||
@ -114,30 +80,3 @@ class CompensationActionTreeCheckboxSelectMultiple(KonovaCodeTreeCheckboxSelectM
|
|||||||
"code_lists__in": [CODELIST_COMPENSATION_ACTION_ID],
|
"code_lists__in": [CODELIST_COMPENSATION_ACTION_ID],
|
||||||
"parent": None,
|
"parent": None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class CompensationStateTreeRadioSelect(KonovaCodeTreeRadioSelect):
|
|
||||||
""" Provides single selection of CompensationState
|
|
||||||
|
|
||||||
"""
|
|
||||||
filter = None
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.filter = {
|
|
||||||
"code_lists__in": [CODELIST_BIOTOPES_ID],
|
|
||||||
"parent": None,
|
|
||||||
"is_archived": False,
|
|
||||||
}
|
|
||||||
|
|
||||||
def get_context(self, name, value, attrs):
|
|
||||||
context = super().get_context(name, value, attrs)
|
|
||||||
codes = KonovaCode.objects.filter(
|
|
||||||
**self.filter,
|
|
||||||
)
|
|
||||||
codes = [
|
|
||||||
parent_code.add_children("short_name")
|
|
||||||
for parent_code in codes
|
|
||||||
]
|
|
||||||
context["codes"] = codes
|
|
||||||
return context
|
|
@ -13,7 +13,6 @@ 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
|
||||||
@ -132,16 +131,6 @@ 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)
|
||||||
@ -182,8 +171,6 @@ 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):
|
||||||
@ -348,7 +335,6 @@ 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):
|
||||||
|
@ -7,10 +7,3 @@ 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"
|
|
||||||
|
@ -9,11 +9,12 @@ from django.http import HttpRequest
|
|||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.html import format_html
|
from django.utils.html import format_html
|
||||||
|
from django.utils.timezone import localtime
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from intervention.filters import InterventionTableFilter
|
from intervention.filters import InterventionTableFilter
|
||||||
from intervention.models import Intervention
|
from intervention.models import Intervention
|
||||||
from konova.utils.message_templates import DATA_CHECKED_ON_TEMPLATE, DATA_IS_UNCHECKED, DATA_CHECKED_PREVIOUSLY_TEMPLATE
|
from konova.sub_settings.django_settings import DEFAULT_DATE_TIME_FORMAT, DEFAULT_DATE_FORMAT
|
||||||
from konova.utils.tables import BaseTable, TableRenderMixin
|
from konova.utils.tables import BaseTable, TableRenderMixin
|
||||||
import django_tables2 as tables
|
import django_tables2 as tables
|
||||||
|
|
||||||
@ -107,21 +108,16 @@ class InterventionTable(BaseTable, TableRenderMixin):
|
|||||||
"""
|
"""
|
||||||
html = ""
|
html = ""
|
||||||
checked = value is not None
|
checked = value is not None
|
||||||
previously_checked = record.get_last_checked_action()
|
tooltip = _("Not checked yet")
|
||||||
tooltip = DATA_IS_UNCHECKED
|
|
||||||
if checked:
|
if checked:
|
||||||
checked_on = value.get_timestamp_str_formatted()
|
value = value.timestamp
|
||||||
tooltip = DATA_CHECKED_ON_TEMPLATE.format(checked_on, record.checked.user)
|
value = localtime(value)
|
||||||
|
checked_on = value.strftime(DEFAULT_DATE_TIME_FORMAT)
|
||||||
|
tooltip = _("Checked on {} by {}").format(checked_on, record.checked.user)
|
||||||
html += self.render_checked_star(
|
html += self.render_checked_star(
|
||||||
tooltip=tooltip,
|
tooltip=tooltip,
|
||||||
icn_filled=checked,
|
icn_filled=checked,
|
||||||
)
|
)
|
||||||
if previously_checked and not checked:
|
|
||||||
checked_on = previously_checked.get_timestamp_str_formatted()
|
|
||||||
tooltip = DATA_CHECKED_PREVIOUSLY_TEMPLATE.format(checked_on, previously_checked.user)
|
|
||||||
html += self.render_previously_checked_star(
|
|
||||||
tooltip=tooltip,
|
|
||||||
)
|
|
||||||
return format_html(html)
|
return format_html(html)
|
||||||
|
|
||||||
def render_d(self, value, record: Intervention):
|
def render_d(self, value, record: Intervention):
|
||||||
@ -135,7 +131,7 @@ class InterventionTable(BaseTable, TableRenderMixin):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
parcels = value.get_underlying_parcels().values_list(
|
parcels = value.get_underlying_parcels().values_list(
|
||||||
"parcel_group__name",
|
"gmrkng",
|
||||||
flat=True
|
flat=True
|
||||||
).distinct()
|
).distinct()
|
||||||
html = render_to_string(
|
html = render_to_string(
|
||||||
@ -160,7 +156,9 @@ class InterventionTable(BaseTable, TableRenderMixin):
|
|||||||
checked = value is not None
|
checked = value is not None
|
||||||
tooltip = _("Not recorded yet")
|
tooltip = _("Not recorded yet")
|
||||||
if checked:
|
if checked:
|
||||||
on = value.get_timestamp_str_formatted()
|
value = value.timestamp
|
||||||
|
value = localtime(value)
|
||||||
|
on = value.strftime(DEFAULT_DATE_TIME_FORMAT)
|
||||||
tooltip = _("Recorded on {} by {}").format(on, record.recorded.user)
|
tooltip = _("Recorded on {} by {}").format(on, record.recorded.user)
|
||||||
html += self.render_bookmark(
|
html += self.render_bookmark(
|
||||||
tooltip=tooltip,
|
tooltip=tooltip,
|
||||||
@ -179,7 +177,9 @@ class InterventionTable(BaseTable, TableRenderMixin):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
html = ""
|
html = ""
|
||||||
has_access = record.is_shared_with(self.user)
|
has_access = value.filter(
|
||||||
|
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"),
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
"""
|
|
||||||
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 %}
|
{% load i18n l10n static fontawesome_5 humanize %}
|
||||||
|
|
||||||
{% block head %}
|
{% block head %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
@ -70,11 +70,6 @@
|
|||||||
<span>
|
<span>
|
||||||
{% fa5_icon 'star' 'far' %}
|
{% fa5_icon 'star' 'far' %}
|
||||||
</span>
|
</span>
|
||||||
{% if last_checked %}
|
|
||||||
<span class="rlp-gd-inv" title="{{last_checked_tooltip}}">
|
|
||||||
{% fa5_icon 'star' 'fas' %}
|
|
||||||
</span>
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="check-star" title="{% trans 'Checked on '%} {{obj.checked.timestamp}} {% trans 'by' %} {{obj.checked.user}}">
|
<span class="check-star" title="{% trans 'Checked on '%} {{obj.checked.timestamp}} {% trans 'by' %} {{obj.checked.user}}">
|
||||||
{% fa5_icon 'star' %}
|
{% fa5_icon 'star' %}
|
||||||
@ -111,21 +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">
|
||||||
{% if obj.modified %}
|
{{obj.created.timestamp|default_if_none:""|naturalday}}
|
||||||
{{obj.modified.timestamp|default_if_none:""}}
|
<br>
|
||||||
<br>
|
{{obj.created.user.username}}
|
||||||
{{obj.modified.user.username}}
|
|
||||||
{% else %}
|
|
||||||
{{obj.created.timestamp|default_if_none:""}}
|
|
||||||
<br>
|
|
||||||
{{obj.created.user.username}}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans 'Shared with' %}</th>
|
<th scope="row">{% trans 'Shared with' %}</th>
|
||||||
<td class="align-middle">
|
<td class="align-middle">
|
||||||
{% for team in obj.shared_teams %}
|
{% for team in obj.teams.all %}
|
||||||
{% include 'user/includes/team_data_modal_button.html' %}
|
{% include 'user/includes/team_data_modal_button.html' %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<hr>
|
<hr>
|
||||||
@ -139,12 +128,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-sm-12 col-md-12 col-lg-12 col-xl-6">
|
<div class="col-sm-12 col-md-12 col-lg-12 col-xl-6">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12">
|
{% include 'map/geom_form.html' %}
|
||||||
{% include 'map/geom_form.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% include 'konova/includes/parcels/parcels.html' %}
|
{% include 'konova/includes/parcels.html' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% include 'konova/includes/comment_card.html' %}
|
{% include 'konova/includes/comment_card.html' %}
|
||||||
|
@ -94,15 +94,20 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-sm-12 col-md-12 col-lg-12 col-xl-6">
|
<div class="col-sm-12 col-md-12 col-lg-12 col-xl-6">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12">
|
{% include 'map/geom_form.html' %}
|
||||||
{% include 'map/geom_form.html' %}
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
{% include 'konova/includes/parcels.html' %}
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6 col-md-6 col-lg-6">
|
||||||
|
<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 class="row">
|
|
||||||
{% include 'konova/includes/parcels/parcels.html' %}
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
{% include 'konova/includes/report/qrcodes.html' %}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -46,7 +46,6 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase):
|
|||||||
test_id = self.create_dummy_string()
|
test_id = self.create_dummy_string()
|
||||||
test_title = self.create_dummy_string()
|
test_title = self.create_dummy_string()
|
||||||
test_geom = self.create_dummy_geometry()
|
test_geom = self.create_dummy_geometry()
|
||||||
geom_json = self.create_geojson(test_geom)
|
|
||||||
|
|
||||||
new_url = reverse("intervention:new", args=())
|
new_url = reverse("intervention:new", args=())
|
||||||
|
|
||||||
@ -60,7 +59,7 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase):
|
|||||||
post_data = {
|
post_data = {
|
||||||
"identifier": test_id,
|
"identifier": test_id,
|
||||||
"title": test_title,
|
"title": test_title,
|
||||||
"geom": geom_json,
|
"geom": test_geom.geojson,
|
||||||
}
|
}
|
||||||
response = self.client_user.post(
|
response = self.client_user.post(
|
||||||
new_url,
|
new_url,
|
||||||
@ -90,30 +89,6 @@ 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
|
||||||
|
|
||||||
|
@ -1,250 +0,0 @@
|
|||||||
"""
|
|
||||||
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,8 +18,7 @@ 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, DATA_CHECKED_PREVIOUSLY_TEMPLATE
|
|
||||||
from konova.utils.user_checks import in_group
|
from konova.utils.user_checks import in_group
|
||||||
|
|
||||||
|
|
||||||
@ -265,18 +264,15 @@ def detail_view(request: HttpRequest, id: str):
|
|||||||
geom_form = SimpleGeomForm(
|
geom_form = SimpleGeomForm(
|
||||||
instance=intervention,
|
instance=intervention,
|
||||||
)
|
)
|
||||||
last_checked = intervention.get_last_checked_action()
|
|
||||||
last_checked_tooltip = ""
|
parcels = intervention.get_underlying_parcels()
|
||||||
if last_checked:
|
|
||||||
last_checked_tooltip = DATA_CHECKED_PREVIOUSLY_TEMPLATE.format(last_checked.get_timestamp_str_formatted(), last_checked.user)
|
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
"obj": intervention,
|
"obj": intervention,
|
||||||
"last_checked": last_checked,
|
|
||||||
"last_checked_tooltip": last_checked_tooltip,
|
|
||||||
"compensations": compensations,
|
"compensations": compensations,
|
||||||
"has_access": is_data_shared,
|
"has_access": is_data_shared,
|
||||||
"geom_form": geom_form,
|
"geom_form": geom_form,
|
||||||
|
"parcels": parcels,
|
||||||
"is_default_member": in_group(_user, DEFAULT_GROUP),
|
"is_default_member": in_group(_user, DEFAULT_GROUP),
|
||||||
"is_zb_member": in_group(_user, ZB_GROUP),
|
"is_zb_member": in_group(_user, ZB_GROUP),
|
||||||
"is_ets_member": in_group(_user, ETS_GROUP),
|
"is_ets_member": in_group(_user, ETS_GROUP),
|
||||||
@ -306,13 +302,6 @@ 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)
|
||||||
@ -704,22 +693,19 @@ def report_view(request:HttpRequest, id: str):
|
|||||||
distinct_deductions = intervention.deductions.all().distinct(
|
distinct_deductions = intervention.deductions.all().distinct(
|
||||||
"account"
|
"account"
|
||||||
)
|
)
|
||||||
qrcode_url = request.build_absolute_uri(reverse("intervention:report", args=(id,)))
|
qrcode_img = generate_qr_code(
|
||||||
qrcode_img = generate_qr_code(qrcode_url, 10)
|
request.build_absolute_uri(reverse("intervention:report", args=(id,))),
|
||||||
qrcode_lanis_url = intervention.get_LANIS_link()
|
10
|
||||||
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": qrcode_img,
|
||||||
"img": qrcode_img,
|
"qrcode_lanis": qrcode_img_lanis,
|
||||||
"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,8 +7,7 @@ Created on: 22.07.21
|
|||||||
"""
|
"""
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from konova.models import Geometry, Deadline, GeometryConflict, Parcel, District, Municipal, ParcelGroup
|
from konova.models import Geometry, Deadline, GeometryConflict, Parcel, District
|
||||||
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
|
||||||
|
|
||||||
@ -17,28 +16,13 @@ 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",
|
||||||
"parcel_group",
|
"gmrkng",
|
||||||
"flr",
|
"flr",
|
||||||
"flrstck_nnr",
|
"flrstck_nnr",
|
||||||
"flrstck_zhlr",
|
"flrstck_zhlr",
|
||||||
@ -48,27 +32,9 @@ 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",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -98,18 +64,6 @@ class DeadlineAdmin(admin.ModelAdmin):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class DeletableObjectMixinAdmin(admin.ModelAdmin):
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
def restore_deleted_data(self, request, queryset):
|
|
||||||
queryset = queryset.filter(
|
|
||||||
deleted__isnull=False
|
|
||||||
)
|
|
||||||
for entry in queryset:
|
|
||||||
entry.deleted.delete()
|
|
||||||
|
|
||||||
|
|
||||||
class BaseResourceAdmin(admin.ModelAdmin):
|
class BaseResourceAdmin(admin.ModelAdmin):
|
||||||
fields = [
|
fields = [
|
||||||
"created",
|
"created",
|
||||||
@ -121,7 +75,7 @@ class BaseResourceAdmin(admin.ModelAdmin):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class BaseObjectAdmin(BaseResourceAdmin, DeletableObjectMixinAdmin):
|
class BaseObjectAdmin(BaseResourceAdmin):
|
||||||
search_fields = [
|
search_fields = [
|
||||||
"identifier",
|
"identifier",
|
||||||
"title",
|
"title",
|
||||||
@ -138,13 +92,18 @@ class BaseObjectAdmin(BaseResourceAdmin, DeletableObjectMixinAdmin):
|
|||||||
"deleted",
|
"deleted",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def restore_deleted_data(self, request, queryset):
|
||||||
|
queryset = queryset.filter(
|
||||||
|
deleted__isnull=False
|
||||||
|
)
|
||||||
|
for entry in queryset:
|
||||||
|
entry.deleted.delete()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Outcommented for a cleaner admin backend on production
|
# Outcommented for a cleaner admin backend on production
|
||||||
#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)
|
||||||
|
@ -53,16 +53,14 @@ class InterventionAutocomplete(Select2QuerySetView):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
user = self.request.user
|
if self.request.user.is_anonymous:
|
||||||
if user.is_anonymous:
|
|
||||||
return Intervention.objects.none()
|
return Intervention.objects.none()
|
||||||
qs = Intervention.objects.filter(
|
qs = Intervention.objects.filter(
|
||||||
Q(deleted=None) &
|
deleted=None,
|
||||||
Q(users__in=[user]) |
|
users__in=[self.request.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) |
|
||||||
@ -97,9 +95,7 @@ class ShareTeamAutocomplete(Select2QuerySetView):
|
|||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
if self.request.user.is_anonymous:
|
if self.request.user.is_anonymous:
|
||||||
return Team.objects.none()
|
return Team.objects.none()
|
||||||
qs = Team.objects.filter(
|
qs = Team.objects.all()
|
||||||
deleted__isnull=True
|
|
||||||
)
|
|
||||||
if self.q:
|
if self.q:
|
||||||
# Due to privacy concerns only a full username match will return the proper user entry
|
# Due to privacy concerns only a full username match will return the proper user entry
|
||||||
qs = qs.filter(
|
qs = qs.filter(
|
||||||
@ -111,29 +107,6 @@ class ShareTeamAutocomplete(Select2QuerySetView):
|
|||||||
return qs
|
return qs
|
||||||
|
|
||||||
|
|
||||||
class TeamAdminAutocomplete(Select2QuerySetView):
|
|
||||||
""" Autocomplete for share with teams
|
|
||||||
|
|
||||||
"""
|
|
||||||
def get_queryset(self):
|
|
||||||
if self.request.user.is_anonymous:
|
|
||||||
return User.objects.none()
|
|
||||||
qs = User.objects.filter(
|
|
||||||
id__in=self.forwarded.get("members", [])
|
|
||||||
).exclude(
|
|
||||||
id__in=self.forwarded.get("admins", [])
|
|
||||||
)
|
|
||||||
if self.q:
|
|
||||||
# Due to privacy concerns only a full username match will return the proper user entry
|
|
||||||
qs = qs.filter(
|
|
||||||
name__icontains=self.q
|
|
||||||
)
|
|
||||||
qs = qs.order_by(
|
|
||||||
"username"
|
|
||||||
)
|
|
||||||
return qs
|
|
||||||
|
|
||||||
|
|
||||||
class KonovaCodeAutocomplete(Select2GroupQuerySetView):
|
class KonovaCodeAutocomplete(Select2GroupQuerySetView):
|
||||||
"""
|
"""
|
||||||
Provides simple autocomplete functionality for codes
|
Provides simple autocomplete functionality for codes
|
||||||
|
@ -145,20 +145,26 @@ class GeoReferencedTableFilterMixin(django_filters.FilterSet):
|
|||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
def _filter_parcel_reference(self, queryset, filter_q) -> QuerySet:
|
def _filter_parcel_reference(self, queryset, name, value, filter_value) -> QuerySet:
|
||||||
""" Filters the parcel entries by a given filter_q
|
""" Filters the parcel entries by a given filter_value.
|
||||||
|
|
||||||
|
filter_value may already include further filter annotations like 'xy__icontains'
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
queryset (QuerySet): The queryset
|
queryset ():
|
||||||
filter_q (Q): The Q-style filter expression
|
name ():
|
||||||
|
value ():
|
||||||
|
filter_value ():
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
_filter = {
|
||||||
|
filter_value: value
|
||||||
|
}
|
||||||
matching_parcels = Parcel.objects.filter(
|
matching_parcels = Parcel.objects.filter(
|
||||||
filter_q
|
**_filter
|
||||||
)
|
)
|
||||||
|
|
||||||
related_geoms = matching_parcels.values(
|
related_geoms = matching_parcels.values(
|
||||||
"geometries"
|
"geometries"
|
||||||
).distinct()
|
).distinct()
|
||||||
@ -179,9 +185,8 @@ class GeoReferencedTableFilterMixin(django_filters.FilterSet):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
matching_districts = District.objects.filter(
|
matching_districts = District.objects.filter(
|
||||||
Q(name__icontains=value) |
|
krs__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
|
||||||
)
|
)
|
||||||
@ -204,10 +209,7 @@ class GeoReferencedTableFilterMixin(django_filters.FilterSet):
|
|||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
queryset = self._filter_parcel_reference(
|
queryset = self._filter_parcel_reference(queryset, name, value, "gmrkng__icontains")
|
||||||
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:
|
||||||
@ -222,10 +224,7 @@ class GeoReferencedTableFilterMixin(django_filters.FilterSet):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
value = value.replace("-", "")
|
value = value.replace("-", "")
|
||||||
queryset = self._filter_parcel_reference(
|
queryset = self._filter_parcel_reference(queryset, name, value, "flr")
|
||||||
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:
|
||||||
@ -240,10 +239,7 @@ class GeoReferencedTableFilterMixin(django_filters.FilterSet):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
value = value.replace("-", "")
|
value = value.replace("-", "")
|
||||||
queryset = self._filter_parcel_reference(
|
queryset = self._filter_parcel_reference(queryset, name, value, "flrstck_zhlr")
|
||||||
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:
|
||||||
@ -258,10 +254,7 @@ class GeoReferencedTableFilterMixin(django_filters.FilterSet):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
value = value.replace("-", "")
|
value = value.replace("-", "")
|
||||||
queryset = self._filter_parcel_reference(
|
queryset = self._filter_parcel_reference(queryset, name, value, "flrstck_nnr")
|
||||||
queryset,
|
|
||||||
Q(flrstck_nnr=value),
|
|
||||||
)
|
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
@ -305,7 +298,7 @@ class ShareableTableFilterMixin(django_filters.FilterSet):
|
|||||||
if not value:
|
if not value:
|
||||||
return queryset.filter(
|
return queryset.filter(
|
||||||
Q(users__in=[self.user]) | # requesting user has access
|
Q(users__in=[self.user]) | # requesting user has access
|
||||||
Q(teams__in=self.user.shared_teams)
|
Q(teams__users__in=[self.user])
|
||||||
).distinct()
|
).distinct()
|
||||||
else:
|
else:
|
||||||
return queryset
|
return queryset
|
||||||
|
125
konova/forms.py
125
konova/forms.py
@ -5,20 +5,18 @@ Contact: michel.peltriaux@sgdnord.rlp.de
|
|||||||
Created on: 16.11.20
|
Created on: 16.11.20
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import json
|
|
||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
|
|
||||||
from bootstrap_modal_forms.forms import BSModalForm
|
from bootstrap_modal_forms.forms import BSModalForm
|
||||||
from bootstrap_modal_forms.utils import is_ajax
|
from bootstrap_modal_forms.utils import is_ajax
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.gis import gdal
|
|
||||||
from django.db.models.fields.files import FieldFile
|
from django.db.models.fields.files import FieldFile
|
||||||
|
|
||||||
from konova.sub_settings.lanis_settings import DEFAULT_SRID_RLP
|
|
||||||
from user.models import User
|
from user.models import User
|
||||||
from django.contrib.gis.forms import MultiPolygonField
|
from django.contrib.gis.forms import OSMWidget, MultiPolygonField
|
||||||
from django.contrib.gis.geos import MultiPolygon, Polygon
|
from django.contrib.gis.geos import MultiPolygon
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.http import HttpRequest, HttpResponseRedirect
|
from django.http import HttpRequest, HttpResponseRedirect
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
@ -59,8 +57,6 @@ 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!
|
||||||
@ -140,38 +136,6 @@ 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(
|
||||||
@ -274,85 +238,41 @@ class SimpleGeomForm(BaseForm):
|
|||||||
""" A geometry form for rendering geometry read-only using a widget
|
""" A geometry form for rendering geometry read-only using a widget
|
||||||
|
|
||||||
"""
|
"""
|
||||||
read_only = True
|
|
||||||
geom = MultiPolygonField(
|
geom = MultiPolygonField(
|
||||||
srid=DEFAULT_SRID_RLP,
|
srid=DEFAULT_SRID,
|
||||||
label=_("Geometry"),
|
label=_("Geometry"),
|
||||||
help_text=_(""),
|
help_text=_(""),
|
||||||
label_suffix="",
|
label_suffix="",
|
||||||
required=False,
|
required=False,
|
||||||
disabled=False,
|
disabled=False,
|
||||||
|
widget=OSMWidget(
|
||||||
|
attrs={
|
||||||
|
"map_width": 600,
|
||||||
|
"map_height": 400,
|
||||||
|
# default_zoom defines the nearest possible zoom level from which the JS automatically
|
||||||
|
# zooms out if geometry requires a larger view port. So define a larger range for smaller geometries
|
||||||
|
"default_zoom": 25,
|
||||||
|
}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.read_only = kwargs.pop("read_only", True)
|
read_only = kwargs.pop("read_only", True)
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
# Initialize geometry
|
# Initialize geometry
|
||||||
try:
|
try:
|
||||||
geom = self.instance.geometry.geom
|
geom = self.instance.geometry.geom
|
||||||
self.empty = geom.empty
|
self.empty = geom.empty
|
||||||
|
|
||||||
if self.empty:
|
|
||||||
raise AttributeError
|
|
||||||
|
|
||||||
geojson = self.instance.geometry.as_feature_collection(srid=DEFAULT_SRID_RLP)
|
|
||||||
geom = json.dumps(geojson)
|
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# If no geometry exists for this form, we simply set the value to None and zoom to the maximum level
|
# If no geometry exists for this form, we simply set the value to None and zoom to the maximum level
|
||||||
geom = ""
|
geom = None
|
||||||
self.empty = True
|
self.empty = True
|
||||||
|
self.fields["geom"].widget.attrs["default_zoom"] = 1
|
||||||
|
|
||||||
self.initialize_form_field("geom", geom)
|
self.initialize_form_field("geom", geom)
|
||||||
|
if read_only:
|
||||||
def is_valid(self):
|
self.fields["geom"].disabled = True
|
||||||
super().is_valid()
|
|
||||||
is_valid = True
|
|
||||||
|
|
||||||
# Get geojson from form
|
|
||||||
geom = self.data["geom"]
|
|
||||||
if geom is None or len(geom) == 0:
|
|
||||||
# empty geometry is a valid geometry
|
|
||||||
return is_valid
|
|
||||||
geom = json.loads(geom)
|
|
||||||
|
|
||||||
# Write submitted data back into form field to make sure invalid geometry
|
|
||||||
# will be rendered again on failed submit
|
|
||||||
self.initialize_form_field("geom", self.data["geom"])
|
|
||||||
|
|
||||||
# Read geojson into gdal geometry
|
|
||||||
# HINT: This can be simplified if the geojson format holds data in epsg:4326 (GDAL provides direct creation for
|
|
||||||
# this case)
|
|
||||||
features = []
|
|
||||||
features_json = geom.get("features", [])
|
|
||||||
for feature in features_json:
|
|
||||||
g = gdal.OGRGeometry(json.dumps(feature.get("geometry", feature)), srs=DEFAULT_SRID_RLP)
|
|
||||||
if g.geom_type not in ["Polygon", "MultiPolygon"]:
|
|
||||||
self.add_error("geom", _("Only surfaces allowed. Points or lines must be buffered."))
|
|
||||||
is_valid = False
|
|
||||||
return is_valid
|
|
||||||
|
|
||||||
polygon = Polygon.from_ewkt(g.ewkt)
|
|
||||||
is_valid = polygon.valid
|
|
||||||
if not is_valid:
|
|
||||||
self.add_error("geom", polygon.valid_reason)
|
|
||||||
return is_valid
|
|
||||||
|
|
||||||
features.append(polygon)
|
|
||||||
form_geom = MultiPolygon(srid=DEFAULT_SRID_RLP)
|
|
||||||
for feature in features:
|
|
||||||
form_geom = form_geom.union(feature)
|
|
||||||
|
|
||||||
# Make sure to convert into a MultiPolygon. Relevant if a single Polygon is provided.
|
|
||||||
if form_geom.geom_type != "MultiPolygon":
|
|
||||||
form_geom = MultiPolygon(form_geom, srid=DEFAULT_SRID_RLP)
|
|
||||||
|
|
||||||
# Write unioned Multipolygon into cleaned data
|
|
||||||
if self.cleaned_data is None:
|
|
||||||
self.cleaned_data = {}
|
|
||||||
self.cleaned_data["geom"] = form_geom.ewkt
|
|
||||||
|
|
||||||
return is_valid
|
|
||||||
|
|
||||||
def save(self, action: UserActionLogEntry):
|
def save(self, action: UserActionLogEntry):
|
||||||
""" Saves the form's geometry
|
""" Saves the form's geometry
|
||||||
@ -490,6 +410,7 @@ 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
|
||||||
}
|
}
|
||||||
@ -677,11 +598,3 @@ class RecordModalForm(BaseModalForm):
|
|||||||
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
|
|
||||||
|
@ -42,8 +42,7 @@ class InterventionMigrater(BaseMigrater):
|
|||||||
intervention.responsible.registration_office = reg_office_code
|
intervention.responsible.registration_office = reg_office_code
|
||||||
self._migrate_responsible_code_to_team(intervention, reg_office_code, "ZB")
|
self._migrate_responsible_code_to_team(intervention, reg_office_code, "ZB")
|
||||||
except ObjectDoesNotExist:
|
except ObjectDoesNotExist:
|
||||||
intervention.responsible.registration_office = None
|
intervention.comment = f"{intervention.comment or ''}\nUnbekannte Zulassungsbehörde: {eiv_reg_off}"
|
||||||
intervention.comment = f"{intervention.comment or ''}\nUneindeutige Zulassungsbehörde: {eiv_reg_off}"
|
|
||||||
intervention.responsible.registration_file_number = eiv_reg_file_num
|
intervention.responsible.registration_file_number = eiv_reg_file_num
|
||||||
|
|
||||||
if eiv_cons_off is not None and eiv_cons_off != 0:
|
if eiv_cons_off is not None and eiv_cons_off != 0:
|
||||||
@ -53,11 +52,10 @@ class InterventionMigrater(BaseMigrater):
|
|||||||
is_leaf=True,
|
is_leaf=True,
|
||||||
code_lists__in=[CODELIST_CONSERVATION_OFFICE_ID],
|
code_lists__in=[CODELIST_CONSERVATION_OFFICE_ID],
|
||||||
)
|
)
|
||||||
intervention.responsible.conservation_office = cons_office_code
|
|
||||||
self._migrate_responsible_code_to_team(intervention, cons_office_code, "ETS")
|
self._migrate_responsible_code_to_team(intervention, cons_office_code, "ETS")
|
||||||
except ObjectDoesNotExist:
|
except ObjectDoesNotExist:
|
||||||
intervention.responsible.conservation_office = None
|
intervention.comment = f"{intervention.comment or ''}\nUnbekannte Eintragungsstelle: {eiv_cons_off}"
|
||||||
intervention.comment = f"{intervention.comment or ''}\nUneindeutige Eintragungsstelle: '{eiv_cons_off}"
|
intervention.responsible.conservation_office = cons_office_code
|
||||||
intervention.responsible.conservation_file_number = eiv_cons_file_num
|
intervention.responsible.conservation_file_number = eiv_cons_file_num
|
||||||
|
|
||||||
if eiv_handler_type is not None and eiv_handler_type != 0:
|
if eiv_handler_type is not None and eiv_handler_type != 0:
|
||||||
@ -67,12 +65,8 @@ class InterventionMigrater(BaseMigrater):
|
|||||||
is_leaf=True,
|
is_leaf=True,
|
||||||
code_lists__in=[CODELIST_HANDLER_ID]
|
code_lists__in=[CODELIST_HANDLER_ID]
|
||||||
)
|
)
|
||||||
if not handler_type_code.is_leaf:
|
|
||||||
handler_type_code = cons_office_code.long_name
|
|
||||||
raise handler_type_code
|
|
||||||
intervention.responsible.handler.type = handler_type_code
|
intervention.responsible.handler.type = handler_type_code
|
||||||
except ObjectDoesNotExist:
|
except ObjectDoesNotExist:
|
||||||
intervention.responsible.handler.type = None
|
|
||||||
intervention.comment = f"{intervention.comment or ''}\nNicht migrierbarer Eingriffsverursacher_TYP: {eiv_handler_type}"
|
intervention.comment = f"{intervention.comment or ''}\nNicht migrierbarer Eingriffsverursacher_TYP: {eiv_handler_type}"
|
||||||
intervention.responsible.handler.detail = eiv_handler_detail
|
intervention.responsible.handler.detail = eiv_handler_detail
|
||||||
intervention.responsible.handler.save()
|
intervention.responsible.handler.save()
|
||||||
|
@ -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, Municipal, ParcelGroup
|
from konova.models import Deadline, Geometry, Parcel, District
|
||||||
from user.models import UserActionLogEntry, UserAction
|
from user.models import UserActionLogEntry, UserAction
|
||||||
|
|
||||||
|
|
||||||
@ -271,26 +271,13 @@ 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 administrative spatial references ===")
|
self._write_warning("=== Sanitize parcels and districts ===")
|
||||||
unrelated_parcels = Parcel.objects.filter(
|
unrelated_parcels = Parcel.objects.filter(
|
||||||
geometries=None,
|
geometries=None,
|
||||||
)
|
)
|
||||||
@ -302,12 +289,16 @@ class Command(BaseKonovaCommand):
|
|||||||
else:
|
else:
|
||||||
self._write_success("No unrelated parcels found.")
|
self._write_success("No unrelated parcels found.")
|
||||||
|
|
||||||
sub_types = [
|
unrelated_districts = District.objects.filter(
|
||||||
District,
|
parcels=None,
|
||||||
Municipal,
|
)
|
||||||
ParcelGroup
|
num_unrelated_districts = unrelated_districts.count()
|
||||||
]
|
if num_unrelated_districts > 0:
|
||||||
for sub_type in sub_types:
|
self._write_error(f"Found {num_unrelated_districts} unrelated district entries. Delete now...")
|
||||||
self.__sanitize_parcel_sub_type(sub_type)
|
unrelated_districts.delete()
|
||||||
|
self._write_success("Unrelated districts deleted.")
|
||||||
|
else:
|
||||||
|
self._write_success("No unrelated districts found.")
|
||||||
|
|
||||||
self._break_line()
|
self._break_line()
|
||||||
|
|
||||||
|
@ -5,13 +5,10 @@ Contact: michel.peltriaux@sgdnord.rlp.de
|
|||||||
Created on: 04.01.22
|
Created on: 04.01.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import datetime
|
|
||||||
from pyexpat import ExpatError
|
from pyexpat import ExpatError
|
||||||
|
|
||||||
from requests.exceptions import ProxyError
|
from requests.exceptions import ProxyError
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
@ -35,11 +32,8 @@ 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'
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if ids is not None:
|
if ids is not None:
|
||||||
|
@ -1,71 +0,0 @@
|
|||||||
# 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'),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,28 +0,0 @@
|
|||||||
# 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),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,48 +0,0 @@
|
|||||||
# 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),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,19 +0,0 @@
|
|||||||
# 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'),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,17 +0,0 @@
|
|||||||
# Generated by Django 3.1.3 on 2022-04-20 08:34
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('konova', '0009_auto_20220411_1004'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddConstraint(
|
|
||||||
model_name='parcel',
|
|
||||||
constraint=models.UniqueConstraint(fields=('district', 'municipal', 'parcel_group', 'flr', 'flrstck_nnr', 'flrstck_zhlr'), name='Unique parcel constraint'),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,25 +0,0 @@
|
|||||||
# Generated by Django 3.1.3 on 2022-04-20 09:01
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('konova', '0010_auto_20220420_1034'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddConstraint(
|
|
||||||
model_name='district',
|
|
||||||
constraint=models.UniqueConstraint(fields=('key', 'name'), name='Unique district constraint'),
|
|
||||||
),
|
|
||||||
migrations.AddConstraint(
|
|
||||||
model_name='municipal',
|
|
||||||
constraint=models.UniqueConstraint(fields=('key', 'name', 'district'), name='Unique municipal constraint'),
|
|
||||||
),
|
|
||||||
migrations.AddConstraint(
|
|
||||||
model_name='parcelgroup',
|
|
||||||
constraint=models.UniqueConstraint(fields=('key', 'name', 'municipal'), name='Unique parcel group constraint'),
|
|
||||||
),
|
|
||||||
]
|
|
@ -5,15 +5,11 @@ Contact: michel.peltriaux@sgdnord.rlp.de
|
|||||||
Created on: 15.11.21
|
Created on: 15.11.21
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import json
|
|
||||||
|
|
||||||
from django.contrib.gis.db.models import MultiPolygonField
|
from django.contrib.gis.db.models import MultiPolygonField
|
||||||
from django.contrib.gis.geos import Polygon
|
from django.db import models
|
||||||
from django.db import models, transaction
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from konova.models import BaseResource, UuidModel
|
from konova.models import BaseResource, UuidModel
|
||||||
from konova.sub_settings.lanis_settings import DEFAULT_SRID_RLP
|
|
||||||
from konova.utils.wfs.spatial import ParcelWFSFetcher
|
from konova.utils.wfs.spatial import ParcelWFSFetcher
|
||||||
|
|
||||||
|
|
||||||
@ -24,9 +20,6 @@ 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()
|
||||||
@ -100,14 +93,13 @@ class Geometry(BaseResource):
|
|||||||
objs += set_objs
|
objs += set_objs
|
||||||
return objs
|
return objs
|
||||||
|
|
||||||
@transaction.atomic
|
|
||||||
def update_parcels(self):
|
def update_parcels(self):
|
||||||
""" Updates underlying parcel information
|
""" Updates underlying parcel information
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from konova.models import Parcel, District, ParcelIntersection, Municipal, ParcelGroup
|
from konova.models import Parcel, District, ParcelIntersection
|
||||||
parcel_fetcher = ParcelWFSFetcher(
|
parcel_fetcher = ParcelWFSFetcher(
|
||||||
geometry_id=self.id,
|
geometry_id=self.id,
|
||||||
)
|
)
|
||||||
@ -118,38 +110,20 @@ class Geometry(BaseResource):
|
|||||||
_now = timezone.now()
|
_now = timezone.now()
|
||||||
underlying_parcels = []
|
underlying_parcels = []
|
||||||
for result in fetched_parcels:
|
for result in fetched_parcels:
|
||||||
parcel_properties = result["properties"]
|
fetched_parcel = result[typename]
|
||||||
# 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 = parcel_properties["flur"].replace("Flur ", "")
|
flr_val = fetched_parcel["ave:flur"].replace("Flur ", "")
|
||||||
district = District.objects.get_or_create(
|
|
||||||
key=parcel_properties["kreisschl"],
|
|
||||||
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(
|
parcel_obj = Parcel.objects.get_or_create(
|
||||||
district=district,
|
gmrkng=fetched_parcel["ave:gemarkung"],
|
||||||
municipal=municipal,
|
|
||||||
parcel_group=parcel_group,
|
|
||||||
flr=flr_val,
|
flr=flr_val,
|
||||||
flrstck_nnr=flrstck_nnr,
|
flrstck_nnr=fetched_parcel['ave:flstnrnen'],
|
||||||
flrstck_zhlr=flrstck_zhlr,
|
flrstck_zhlr=fetched_parcel['ave:flstnrzae'],
|
||||||
|
)[0]
|
||||||
|
district = District.objects.get_or_create(
|
||||||
|
gmnd=fetched_parcel["ave:gemeinde"],
|
||||||
|
krs=fetched_parcel["ave:kreis"],
|
||||||
)[0]
|
)[0]
|
||||||
parcel_obj.district = district
|
parcel_obj.district = district
|
||||||
parcel_obj.updated_on = _now
|
parcel_obj.updated_on = _now
|
||||||
@ -157,7 +131,6 @@ class Geometry(BaseResource):
|
|||||||
underlying_parcels.append(parcel_obj)
|
underlying_parcels.append(parcel_obj)
|
||||||
|
|
||||||
# Update the linked parcels
|
# Update the linked parcels
|
||||||
self.parcels.clear()
|
|
||||||
self.parcels.set(underlying_parcels)
|
self.parcels.set(underlying_parcels)
|
||||||
|
|
||||||
# Set the calculated_on intermediate field, so this related data will be found on lookups
|
# Set the calculated_on intermediate field, so this related data will be found on lookups
|
||||||
@ -178,55 +151,17 @@ class Geometry(BaseResource):
|
|||||||
Returns:
|
Returns:
|
||||||
parcels (QuerySet): The related parcels as queryset
|
parcels (QuerySet): The related parcels as queryset
|
||||||
"""
|
"""
|
||||||
|
|
||||||
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(
|
||||||
"municipal__name",
|
"gmrkng",
|
||||||
)
|
)
|
||||||
|
|
||||||
return parcels
|
return parcels
|
||||||
|
|
||||||
def count_underlying_parcels(self):
|
|
||||||
""" Getter for number of underlying parcels
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
num_parcels = self.parcels.filter(
|
|
||||||
parcelintersection__calculated_on__isnull=False,
|
|
||||||
).count()
|
|
||||||
return num_parcels
|
|
||||||
|
|
||||||
def as_feature_collection(self, srid=DEFAULT_SRID_RLP):
|
|
||||||
""" Returns a FeatureCollection structure holding all polygons of the MultiPolygon as single features
|
|
||||||
|
|
||||||
This method is used to convert a single MultiPolygon into multiple Polygons, which can be used as separated
|
|
||||||
features in the NETGIS map client.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
srid (int): The spatial reference system identifier to be transformed to
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
geojson (dict): The FeatureCollection json (as dict)
|
|
||||||
"""
|
|
||||||
geom = self.geom
|
|
||||||
geom.transform(ct=srid)
|
|
||||||
|
|
||||||
polygons = []
|
|
||||||
for coords in geom.coords:
|
|
||||||
p = Polygon(coords[0], srid=geom.srid)
|
|
||||||
polygons.append(p)
|
|
||||||
geojson = {
|
|
||||||
"type": "FeatureCollection",
|
|
||||||
"features": [
|
|
||||||
json.loads(x.geojson) for x in polygons
|
|
||||||
]
|
|
||||||
}
|
|
||||||
return geojson
|
|
||||||
|
|
||||||
|
|
||||||
class GeometryConflict(UuidModel):
|
class GeometryConflict(UuidModel):
|
||||||
"""
|
"""
|
||||||
|
@ -87,15 +87,25 @@ class BaseResource(UuidModel):
|
|||||||
super().delete()
|
super().delete()
|
||||||
|
|
||||||
|
|
||||||
class DeletableObjectMixin(models.Model):
|
class BaseObject(BaseResource):
|
||||||
""" Wraps deleted field and related functionality
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
A basic object model, which specifies BaseResource.
|
||||||
|
|
||||||
|
Mainly used for intervention, compensation, ecoaccount
|
||||||
|
"""
|
||||||
|
identifier = models.CharField(max_length=1000, null=True, blank=True)
|
||||||
|
title = models.CharField(max_length=1000, null=True, blank=True)
|
||||||
deleted = models.ForeignKey("user.UserActionLogEntry", on_delete=models.SET_NULL, null=True, blank=True, related_name='+')
|
deleted = models.ForeignKey("user.UserActionLogEntry", on_delete=models.SET_NULL, null=True, blank=True, related_name='+')
|
||||||
|
comment = models.TextField(null=True, blank=True)
|
||||||
|
log = models.ManyToManyField("user.UserActionLogEntry", blank=True, help_text="Keeps all user actions of an object", editable=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def set_status_messages(self, request: HttpRequest):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
def mark_as_deleted(self, user, send_mail: bool = True):
|
def mark_as_deleted(self, user, send_mail: bool = True):
|
||||||
""" Mark an entry as deleted
|
""" Mark an entry as deleted
|
||||||
|
|
||||||
@ -130,25 +140,6 @@ class DeletableObjectMixin(models.Model):
|
|||||||
|
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
|
|
||||||
class BaseObject(BaseResource, DeletableObjectMixin):
|
|
||||||
"""
|
|
||||||
A basic object model, which specifies BaseResource.
|
|
||||||
|
|
||||||
Mainly used for intervention, compensation, ecoaccount
|
|
||||||
"""
|
|
||||||
identifier = models.CharField(max_length=1000, null=True, blank=True)
|
|
||||||
title = models.CharField(max_length=1000, null=True, blank=True)
|
|
||||||
comment = models.TextField(null=True, blank=True)
|
|
||||||
log = models.ManyToManyField("user.UserActionLogEntry", blank=True, help_text="Keeps all user actions of an object", editable=False)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def set_status_messages(self, request: HttpRequest):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def mark_as_edited(self, performing_user, request: HttpRequest = None, edit_comment: str = None):
|
def mark_as_edited(self, performing_user, request: HttpRequest = None, edit_comment: str = None):
|
||||||
""" In case the object or a related object changed the log history needs to be updated
|
""" In case the object or a related object changed the log history needs to be updated
|
||||||
|
|
||||||
@ -298,8 +289,6 @@ 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()
|
||||||
@ -346,15 +335,6 @@ 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
|
||||||
@ -417,20 +397,6 @@ class CheckableObjectMixin(models.Model):
|
|||||||
self.log.add(action)
|
self.log.add(action)
|
||||||
return action
|
return action
|
||||||
|
|
||||||
def get_last_checked_action(self):
|
|
||||||
""" Getter for the most recent checked action on the log
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
previously_checked (UserActionLogEntry): The most recent checked action
|
|
||||||
"""
|
|
||||||
from user.models import UserAction
|
|
||||||
previously_checked = self.log.filter(
|
|
||||||
action=UserAction.CHECKED
|
|
||||||
).order_by(
|
|
||||||
"-timestamp"
|
|
||||||
).first()
|
|
||||||
return previously_checked
|
|
||||||
|
|
||||||
|
|
||||||
class ShareableObjectMixin(models.Model):
|
class ShareableObjectMixin(models.Model):
|
||||||
# Users having access on this object
|
# Users having access on this object
|
||||||
@ -493,8 +459,8 @@ class ShareableObjectMixin(models.Model):
|
|||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
directly_shared = self.shared_users.filter(id=user.id).exists()
|
directly_shared = self.users.filter(id=user.id).exists()
|
||||||
team_shared = self.shared_teams.filter(
|
team_shared = self.teams.filter(
|
||||||
users__in=[user]
|
users__in=[user]
|
||||||
).exists()
|
).exists()
|
||||||
is_shared = directly_shared or team_shared
|
is_shared = directly_shared or team_shared
|
||||||
@ -631,9 +597,7 @@ class ShareableObjectMixin(models.Model):
|
|||||||
Returns:
|
Returns:
|
||||||
teams (QuerySet)
|
teams (QuerySet)
|
||||||
"""
|
"""
|
||||||
return self.teams.filter(
|
return self.teams.all()
|
||||||
deleted__isnull=True
|
|
||||||
)
|
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def get_share_url(self):
|
def get_share_url(self):
|
||||||
@ -644,26 +608,6 @@ 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)
|
||||||
@ -677,21 +621,10 @@ class GeoReferencedMixin(models.Model):
|
|||||||
Returns:
|
Returns:
|
||||||
parcels (Iterable): An empty list or a Queryset
|
parcels (Iterable): An empty list or a Queryset
|
||||||
"""
|
"""
|
||||||
result = []
|
|
||||||
if self.geometry is not None:
|
if self.geometry is not None:
|
||||||
result = self.geometry.get_underlying_parcels()
|
return self.geometry.get_underlying_parcels()
|
||||||
return result
|
else:
|
||||||
|
return []
|
||||||
def count_underlying_parcels(self):
|
|
||||||
""" Getter for number of underlying parcels
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
result = 0
|
|
||||||
if self.geometry is not None:
|
|
||||||
result = self.geometry.count_underlying_parcels()
|
|
||||||
return result
|
|
||||||
|
|
||||||
def set_geometry_conflict_message(self, request: HttpRequest):
|
def set_geometry_conflict_message(self, request: HttpRequest):
|
||||||
if self.geometry is None:
|
if self.geometry is None:
|
||||||
|
@ -10,98 +10,8 @@ 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"
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
constraints = [
|
|
||||||
models.UniqueConstraint(
|
|
||||||
fields=[
|
|
||||||
"key",
|
|
||||||
"name",
|
|
||||||
],
|
|
||||||
name="Unique district constraint"
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class Municipal(UuidModel, AdministrativeSpatialReference):
|
|
||||||
""" The model Municipal refers to "Gemeinde"
|
|
||||||
|
|
||||||
"""
|
|
||||||
district = models.ForeignKey(
|
|
||||||
District,
|
|
||||||
on_delete=models.SET_NULL,
|
|
||||||
null=True,
|
|
||||||
blank=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
constraints = [
|
|
||||||
models.UniqueConstraint(
|
|
||||||
fields=[
|
|
||||||
"key",
|
|
||||||
"name",
|
|
||||||
"district",
|
|
||||||
],
|
|
||||||
name="Unique municipal constraint"
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
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 Meta:
|
|
||||||
constraints = [
|
|
||||||
models.UniqueConstraint(
|
|
||||||
fields=[
|
|
||||||
"key",
|
|
||||||
"name",
|
|
||||||
"municipal",
|
|
||||||
],
|
|
||||||
name="Unique parcel group constraint"
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class Parcel(UuidModel):
|
class Parcel(UuidModel):
|
||||||
""" The Parcel model holds administrative data on covered properties.
|
""" The Parcel model holds administrative data on the 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.
|
||||||
@ -114,49 +24,59 @@ 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")
|
||||||
municipal = models.ForeignKey("konova.Municipal", on_delete=models.SET_NULL, null=True, blank=True, related_name="parcels")
|
gmrkng = models.CharField(
|
||||||
parcel_group = models.ForeignKey(
|
max_length=1000,
|
||||||
"konova.ParcelGroup",
|
|
||||||
on_delete=models.SET_NULL,
|
|
||||||
help_text="Gemarkung",
|
help_text="Gemarkung",
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
related_name="parcels"
|
|
||||||
)
|
)
|
||||||
flr = models.IntegerField(
|
flrstck_nnr = models.CharField(
|
||||||
help_text="Flur",
|
max_length=1000,
|
||||||
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.IntegerField(
|
flrstck_zhlr = models.CharField(
|
||||||
|
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)
|
||||||
|
|
||||||
class Meta:
|
def __str__(self):
|
||||||
constraints = [
|
return f"{self.gmrkng} | {self.flr} | {self.flrstck_zhlr} | {self.flrstck_nnr}"
|
||||||
models.UniqueConstraint(
|
|
||||||
fields=[
|
|
||||||
"district",
|
class District(UuidModel):
|
||||||
"municipal",
|
""" The model District holds more coarse information, such as Kreis, Verbandsgemeinde and Gemeinde.
|
||||||
"parcel_group",
|
|
||||||
"flr",
|
There might be the case that a geometry lies on a hundred Parcel entries but only on one District entry.
|
||||||
"flrstck_nnr",
|
Therefore a geometry can have a lot of relations to Parcel entries but only a few or only a single one to one
|
||||||
"flrstck_zhlr",
|
District.
|
||||||
],
|
|
||||||
name="Unique parcel constraint"
|
"""
|
||||||
)
|
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):
|
def __str__(self):
|
||||||
return f"{self.parcel_group} | {self.flr} | {self.flrstck_zhlr} | {self.flrstck_nnr}"
|
return f"{self.gmnd} | {self.krs}"
|
||||||
|
|
||||||
|
|
||||||
class ParcelIntersection(UuidModel):
|
class ParcelIntersection(UuidModel):
|
||||||
|
@ -263,12 +263,3 @@ Similar to bootstraps 'shadow-lg'
|
|||||||
}
|
}
|
||||||
|
|
||||||
*/
|
*/
|
||||||
.collapse-icn > i{
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
.collapsed .collapse-icn > i{
|
|
||||||
transform: rotate(-90deg);
|
|
||||||
}
|
|
||||||
.tree-label.badge{
|
|
||||||
font-size: 90%;
|
|
||||||
}
|
|
@ -11,7 +11,6 @@ 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(
|
||||||
@ -46,8 +45,8 @@ ALLOWED_HOSTS = [
|
|||||||
LOGIN_URL = "/login/"
|
LOGIN_URL = "/login/"
|
||||||
|
|
||||||
# Session settings
|
# Session settings
|
||||||
SESSION_COOKIE_AGE = 60 * 60 # 60 minutes
|
#SESSION_COOKIE_AGE = 30 * 60 # 30 minutes
|
||||||
SESSION_SAVE_EVERY_REQUEST = True
|
#SESSION_SAVE_EVERY_REQUEST = True
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
@ -163,15 +162,9 @@ 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
|
||||||
@ -191,8 +184,6 @@ STATIC_URL = '/static/'
|
|||||||
STATIC_ROOT = os.path.join(BASE_DIR, "static")
|
STATIC_ROOT = os.path.join(BASE_DIR, "static")
|
||||||
STATICFILES_DIRS = [
|
STATICFILES_DIRS = [
|
||||||
os.path.join(BASE_DIR, 'konova/static'),
|
os.path.join(BASE_DIR, 'konova/static'),
|
||||||
os.path.join(BASE_DIR, 'templates/map/client'), # NETGIS map client files
|
|
||||||
os.path.join(BASE_DIR, 'templates/map/client/libs'), # NETGIS map client files
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# DJANGO DEBUG TOOLBAR
|
# DJANGO DEBUG TOOLBAR
|
||||||
|
@ -19,6 +19,6 @@ PAGE_SIZE_OPTIONS_TUPLES = [
|
|||||||
(50, 50),
|
(50, 50),
|
||||||
(100, 100),
|
(100, 100),
|
||||||
]
|
]
|
||||||
PAGE_SIZE_DEFAULT = 10
|
PAGE_SIZE_DEFAULT = 5
|
||||||
PAGE_SIZE_MAX = 100
|
PAGE_SIZE_MAX = 100
|
||||||
PAGE_DEFAULT = 1
|
PAGE_DEFAULT = 1
|
||||||
|
32
konova/templates/konova/includes/parcel_table.html
Normal file
32
konova/templates/konova/includes/parcel_table.html
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{% 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>
|
@ -8,7 +8,7 @@
|
|||||||
</h5>
|
</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div hx-trigger="load, every 5s" hx-get="{% url 'geometry-parcels' geom_form.instance.geometry.id %}">
|
<div hx-trigger="every 2s" hx-get="{% url 'geometry-parcels' geom_form.instance.geometry.id %}" title="{% trans 'Loading...' %}">
|
||||||
<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>
|
@ -1,22 +0,0 @@
|
|||||||
{% 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 %}
|
|
@ -1,49 +0,0 @@
|
|||||||
{% 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 %}
|
|
||||||
<div>
|
|
||||||
<h4 class="">
|
|
||||||
<span class="badge rlp-r">{{num_parcels}}</span>
|
|
||||||
{% trans 'Parcels found' %}</h4>
|
|
||||||
</div>
|
|
||||||
<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>
|
|
@ -1,19 +0,0 @@
|
|||||||
{% 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>
|
|
@ -2,23 +2,18 @@
|
|||||||
|
|
||||||
{% for code in codes %}
|
{% for code in codes %}
|
||||||
<div class="ml-4 tree-element">
|
<div class="ml-4 tree-element">
|
||||||
<label class="tree-label collapsed" role="{% if not code.is_leaf%}button{% endif %}" for="input_{{code.pk|unlocalize}}" id="{{code.pk|unlocalize}}" data-toggle="collapse" data-target="#children_{{code.pk|unlocalize}}" aria-expanded="true" aria-controls="children_{{code.pk|unlocalize}}">
|
<label class="tree-label" role="{% if not code.is_leaf%}button{% endif %}" for="input_{{code.pk|unlocalize}}" id="{{code.pk|unlocalize}}" data-toggle="collapse" data-target="#children_{{code.pk|unlocalize}}" aria-expanded="true" aria-controls="children_{{code.pk|unlocalize}}">
|
||||||
{% if code.is_leaf%}
|
{% if code.is_leaf%}
|
||||||
<input class="tree-input" id="input_{{code.pk|unlocalize}}" name="{{ widget.name }}" type="checkbox" value="{{code.pk|unlocalize}}" {% if code.pk|unlocalize in widget.value %}checked{% endif %}/>
|
<input class="tree-input" id="input_{{code.pk|unlocalize}}" name="{{ widget.name }}" type="checkbox" value="{{code.pk|unlocalize}}" {% if code.pk|unlocalize in widget.value %}checked{% endif %}/>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="collapse-icn">
|
{% fa5_icon 'angle-right' %}
|
||||||
{% fa5_icon 'angle-down' %}
|
|
||||||
</span>
|
|
||||||
{% endif %}
|
|
||||||
{% if code.short_name %}
|
|
||||||
({{code.short_name}})
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{code.long_name}}
|
{{code.long_name}}
|
||||||
</label>
|
</label>
|
||||||
{% if not code.is_leaf %}
|
{% if not code.is_leaf %}
|
||||||
<div id="children_{{code.pk|unlocalize}}" data-toggle="collapse" class="collapse tree-element-children">
|
<div id="children_{{code.pk|unlocalize}}" data-toggle="collapse" class="collapse tree-element-children">
|
||||||
{% with code.children as codes %}
|
{% with code.children as codes %}
|
||||||
{% include 'konova/widgets/tree/checkbox/checkbox-tree-select-content.html' %}
|
{% include 'konova/widgets/checkbox-tree-select-content.html' %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
@ -5,7 +5,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="tree-root">
|
<div id="tree-root">
|
||||||
{% include 'konova/widgets/tree/checkbox/checkbox-tree-select-content.html' %}
|
{% include 'konova/widgets/checkbox-tree-select-content.html' %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -47,12 +47,9 @@
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
if(val.length > 0){
|
if(val.length > 0){
|
||||||
// Hide everything
|
|
||||||
allTreeElements.hide()
|
allTreeElements.hide()
|
||||||
// Now show again everything matching the query
|
|
||||||
allTreeElementsContain.show()
|
allTreeElementsContain.show()
|
||||||
}else{
|
}else{
|
||||||
// Show everything if no query exists
|
|
||||||
allTreeElements.show()
|
allTreeElements.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,25 +0,0 @@
|
|||||||
{% load l10n fontawesome_5 %}
|
|
||||||
{% for code in codes %}
|
|
||||||
<div class="ml-4 tree-element">
|
|
||||||
<label class="tree-label collapsed" role="{% if not code.is_leaf%}button{% endif %}" for="input_{{code.pk|unlocalize}}" id="{{code.pk|unlocalize}}" data-toggle="collapse" data-target="#children_{{code.pk|unlocalize}}" aria-expanded="true" aria-controls="children_{{code.pk|unlocalize}}">
|
|
||||||
{% if code.is_leaf%}
|
|
||||||
<input class="tree-input" id="input_{{code.pk|unlocalize}}" name="{{ widget.name }}" type="radio" value="{{code.pk|unlocalize}}" {% if code.pk|unlocalize in widget.value %}checked{% endif %}/>
|
|
||||||
{% else %}
|
|
||||||
<span class="collapse-icn">
|
|
||||||
{% fa5_icon 'angle-down' %}
|
|
||||||
</span>
|
|
||||||
{% endif %}
|
|
||||||
{% if code.short_name %}
|
|
||||||
({{code.short_name}})
|
|
||||||
{% endif %}
|
|
||||||
{{code.long_name}}
|
|
||||||
</label>
|
|
||||||
{% if not code.is_leaf %}
|
|
||||||
<div id="children_{{code.pk|unlocalize}}" data-toggle="collapse" class="collapse tree-element-children">
|
|
||||||
{% with code.children as codes %}
|
|
||||||
{% include 'konova/widgets/tree/radio/radio-tree-select-content.html' %}
|
|
||||||
{% endwith %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
@ -1,62 +0,0 @@
|
|||||||
{% load i18n %}
|
|
||||||
|
|
||||||
<div class="ml-4 mb-4">
|
|
||||||
<input id="tree-search-input" class="form-control" type="text" placeholder="{% trans 'Search' %}"/>
|
|
||||||
</div>
|
|
||||||
<div id="tree-root">
|
|
||||||
{% include 'konova/widgets/tree/radio/radio-tree-select-content.html' %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
function toggleSelectedCssClass(element){
|
|
||||||
element = $(element);
|
|
||||||
var cssClass = "badge rlp-r"
|
|
||||||
|
|
||||||
// Find all already tagged input elements and reset them to be untagged
|
|
||||||
var allTaggedInputs = $("#tree-root").find(".badge.rlp-r")
|
|
||||||
allTaggedInputs.removeClass(cssClass)
|
|
||||||
|
|
||||||
// Find all parents of selected element
|
|
||||||
var parentElements = element.parents(".tree-element-children")
|
|
||||||
|
|
||||||
// Tag parents of element
|
|
||||||
var parentLabels = parentElements.siblings(".tree-label");
|
|
||||||
parentLabels.addClass(cssClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
function changeHandler(event){
|
|
||||||
toggleSelectedCssClass(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
function searchInputHandler(event){
|
|
||||||
var elem = $(this);
|
|
||||||
var val = elem.val()
|
|
||||||
var allTreeElements = $(".tree-element")
|
|
||||||
var allTreeElementsContain = $(".tree-element").filter(function(){
|
|
||||||
var reg = new RegExp(val, "i");
|
|
||||||
return reg.test($(this).text());
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if(val.length > 0){
|
|
||||||
// Hide everything
|
|
||||||
allTreeElements.hide()
|
|
||||||
// Now show again everything matching the query
|
|
||||||
allTreeElementsContain.show()
|
|
||||||
}else{
|
|
||||||
// Show everything if no query exists
|
|
||||||
allTreeElements.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add event listener on search input
|
|
||||||
$("#tree-search-input").keyup(searchInputHandler)
|
|
||||||
|
|
||||||
// Add event listener on changed checkboxes
|
|
||||||
$(".tree-input").change(changeHandler);
|
|
||||||
|
|
||||||
// initialize all pre-checked checkboxes (e.g. on an edit form)
|
|
||||||
var preCheckedElements = $(".tree-input:checked");
|
|
||||||
preCheckedElements.each(function (index, element){
|
|
||||||
toggleSelectedCssClass(element);
|
|
||||||
})
|
|
||||||
</script>
|
|
@ -72,7 +72,6 @@ class AutocompleteTestCase(BaseTestCase):
|
|||||||
"codes-conservation-office-autocomplete",
|
"codes-conservation-office-autocomplete",
|
||||||
"share-user-autocomplete",
|
"share-user-autocomplete",
|
||||||
"share-team-autocomplete",
|
"share-team-autocomplete",
|
||||||
"team-admin-autocomplete",
|
|
||||||
]
|
]
|
||||||
for test in tests:
|
for test in tests:
|
||||||
self.client.login(username=self.superuser.username, password=self.superuser_pw)
|
self.client.login(username=self.superuser.username, password=self.superuser_pw)
|
||||||
|
@ -6,11 +6,9 @@ Created on: 26.10.21
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
import datetime
|
import datetime
|
||||||
import json
|
|
||||||
|
|
||||||
from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID
|
from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID
|
||||||
from ema.models import Ema
|
from ema.models import Ema
|
||||||
from konova.sub_settings.lanis_settings import DEFAULT_SRID_RLP
|
|
||||||
from user.models import User, Team
|
from user.models import User, Team
|
||||||
from django.contrib.auth.models import Group
|
from django.contrib.auth.models import Group
|
||||||
from django.contrib.gis.geos import MultiPolygon, Polygon
|
from django.contrib.gis.geos import MultiPolygon, Polygon
|
||||||
@ -274,6 +272,7 @@ class BaseTestCase(TestCase):
|
|||||||
team = Team.objects.get_or_create(
|
team = Team.objects.get_or_create(
|
||||||
name="Testteam",
|
name="Testteam",
|
||||||
description="Testdescription",
|
description="Testdescription",
|
||||||
|
admin=self.superuser,
|
||||||
)[0]
|
)[0]
|
||||||
team.users.add(self.superuser)
|
team.users.add(self.superuser)
|
||||||
|
|
||||||
@ -288,28 +287,8 @@ class BaseTestCase(TestCase):
|
|||||||
"""
|
"""
|
||||||
polygon = Polygon.from_bbox((7.592449, 50.359385, 7.593382, 50.359874))
|
polygon = Polygon.from_bbox((7.592449, 50.359385, 7.593382, 50.359874))
|
||||||
polygon.srid = 4326
|
polygon.srid = 4326
|
||||||
polygon = polygon.transform(DEFAULT_SRID_RLP, clone=True)
|
polygon = polygon.transform(3857, clone=True)
|
||||||
return MultiPolygon(polygon, srid=DEFAULT_SRID_RLP)
|
return MultiPolygon(polygon, srid=3857) # 3857 is the default srid used for MultiPolygonField in the form
|
||||||
|
|
||||||
def create_geojson(self, geometry):
|
|
||||||
""" Creates a default structure including geojson from a geometry
|
|
||||||
|
|
||||||
Args:
|
|
||||||
geometry ():
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
geom_json = {
|
|
||||||
"features": [
|
|
||||||
{
|
|
||||||
"type": "Feature",
|
|
||||||
"geometry": json.loads(geometry.geojson),
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
geom_json = json.dumps(geom_json)
|
|
||||||
return geom_json
|
|
||||||
|
|
||||||
def create_dummy_handler(self) -> Handler:
|
def create_dummy_handler(self) -> Handler:
|
||||||
""" Creates a Handler
|
""" Creates a Handler
|
||||||
@ -431,12 +410,11 @@ class BaseTestCase(TestCase):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if geom1.srid != geom2.srid:
|
if geom1.srid != geom2.srid:
|
||||||
tolerance = 0.001
|
|
||||||
# Due to prior possible transformation of any of these geometries, we need to make sure there exists a
|
# Due to prior possible transformation of any of these geometries, we need to make sure there exists a
|
||||||
# transformation from one coordinate system into the other, which is valid
|
# transformation from one coordinate system into the other, which is valid
|
||||||
geom1_t = geom1.transform(geom2.srid, clone=True)
|
geom1_t = geom1.transform(geom2.srid, clone=True)
|
||||||
geom2_t = geom2.transform(geom1.srid, clone=True)
|
geom2_t = geom2.transform(geom1.srid, clone=True)
|
||||||
self.assertTrue(geom1_t.equals_exact(geom2, tolerance) or geom2_t.equals_exact(geom1, tolerance))
|
self.assertTrue(geom1_t.equals(geom2) or geom2_t.equals(geom1))
|
||||||
else:
|
else:
|
||||||
self.assertTrue(geom1.equals(geom2))
|
self.assertTrue(geom1.equals(geom2))
|
||||||
|
|
||||||
|
@ -21,10 +21,10 @@ from konova.autocompletes import EcoAccountAutocomplete, \
|
|||||||
InterventionAutocomplete, CompensationActionCodeAutocomplete, BiotopeCodeAutocomplete, LawCodeAutocomplete, \
|
InterventionAutocomplete, CompensationActionCodeAutocomplete, BiotopeCodeAutocomplete, LawCodeAutocomplete, \
|
||||||
RegistrationOfficeCodeAutocomplete, ConservationOfficeCodeAutocomplete, ProcessTypeCodeAutocomplete, \
|
RegistrationOfficeCodeAutocomplete, ConservationOfficeCodeAutocomplete, ProcessTypeCodeAutocomplete, \
|
||||||
ShareUserAutocomplete, BiotopeExtraCodeAutocomplete, CompensationActionDetailCodeAutocomplete, \
|
ShareUserAutocomplete, BiotopeExtraCodeAutocomplete, CompensationActionDetailCodeAutocomplete, \
|
||||||
ShareTeamAutocomplete, HandlerCodeAutocomplete, TeamAdminAutocomplete, CompensationHandlerCodeAutocomplete
|
ShareTeamAutocomplete, HandlerCodeAutocomplete, CompensationHandlerCodeAutocomplete
|
||||||
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, get_geom_parcels_content, map_client_proxy_view
|
from konova.views import logout_view, home_view, get_geom_parcels
|
||||||
|
|
||||||
sso_client = KonovaSSOClient(SSO_SERVER, SSO_PUBLIC_KEY, SSO_PRIVATE_KEY)
|
sso_client = KonovaSSOClient(SSO_SERVER, SSO_PUBLIC_KEY, SSO_PRIVATE_KEY)
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
@ -40,9 +40,7 @@ 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"),
|
|
||||||
path('client/proxy', map_client_proxy_view, name="map-client-proxy"),
|
|
||||||
|
|
||||||
# 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"),
|
||||||
@ -59,7 +57,6 @@ urlpatterns = [
|
|||||||
path("atcmplt/codes/comp/handler", CompensationHandlerCodeAutocomplete.as_view(), name="codes-compensation-handler-autocomplete"),
|
path("atcmplt/codes/comp/handler", CompensationHandlerCodeAutocomplete.as_view(), name="codes-compensation-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"),
|
||||||
path("atcmplt/team/admin", TeamAdminAutocomplete.as_view(), name="team-admin-autocomplete"),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
|
@ -17,7 +17,6 @@ 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")
|
||||||
@ -81,8 +80,3 @@ GEOMETRY_CONFLICT_WITH_TEMPLATE = _("Geometry conflict detected with {}")
|
|||||||
|
|
||||||
# INTERVENTION
|
# INTERVENTION
|
||||||
INTERVENTION_HAS_REVOCATIONS_TEMPLATE = _("This intervention has {} revocations")
|
INTERVENTION_HAS_REVOCATIONS_TEMPLATE = _("This intervention has {} revocations")
|
||||||
|
|
||||||
# CHECKED
|
|
||||||
DATA_CHECKED_ON_TEMPLATE = _("Checked on {} by {}")
|
|
||||||
DATA_CHECKED_PREVIOUSLY_TEMPLATE = _("Data has changed since last check on {} by {}")
|
|
||||||
DATA_IS_UNCHECKED = _("Current data not checked yet")
|
|
||||||
|
@ -112,17 +112,6 @@ class BaseTable(tables.tables.Table):
|
|||||||
icon
|
icon
|
||||||
)
|
)
|
||||||
|
|
||||||
def render_previously_checked_star(self, tooltip: str = None):
|
|
||||||
"""
|
|
||||||
Returns a star icon for a check action in the past
|
|
||||||
"""
|
|
||||||
icon = "fas fa-star rlp-gd-inv"
|
|
||||||
return format_html(
|
|
||||||
"<em title='{}' class='{}'></em>",
|
|
||||||
tooltip,
|
|
||||||
icon
|
|
||||||
)
|
|
||||||
|
|
||||||
def render_bookmark(self, tooltip: str = None, icn_filled: bool = False):
|
def render_bookmark(self, tooltip: str = None, icn_filled: bool = False):
|
||||||
"""
|
"""
|
||||||
Returns a bookmark icon
|
Returns a bookmark icon
|
||||||
|
@ -5,13 +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
|
||||||
from django.contrib.gis.db.models.functions import AsGML, Transform, MakeValid
|
import xmltodict
|
||||||
|
from django.contrib.gis.db.models.functions import AsGML, Transform
|
||||||
from requests.auth import HTTPDigestAuth
|
from requests.auth import HTTPDigestAuth
|
||||||
|
|
||||||
from konova.settings import DEFAULT_SRID_RLP, PARCEL_WFS_USER, PARCEL_WFS_PW, PROXIES
|
from konova.settings import DEFAULT_SRID_RLP, PARCEL_WFS_USER, PARCEL_WFS_PW, PROXIES
|
||||||
@ -91,7 +90,7 @@ class ParcelWFSFetcher(AbstractWFSFetcher):
|
|||||||
).annotate(
|
).annotate(
|
||||||
transformed=Transform(srid=filter_srid, expression="geom")
|
transformed=Transform(srid=filter_srid, expression="geom")
|
||||||
).annotate(
|
).annotate(
|
||||||
gml=AsGML(MakeValid('transformed'))
|
gml=AsGML('transformed')
|
||||||
).first().gml
|
).first().gml
|
||||||
spatial_filter = f"<Filter><{geometry_operation}><PropertyName>{self.geometry_property_name}</PropertyName>{geom_gml}</{geometry_operation}></Filter>"
|
spatial_filter = f"<Filter><{geometry_operation}><PropertyName>{self.geometry_property_name}</PropertyName>{geom_gml}</{geometry_operation}></Filter>"
|
||||||
return spatial_filter
|
return spatial_filter
|
||||||
@ -116,7 +115,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}" outputFormat="application/json; subtype=geojson"><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}"><wfs:Query typeNames="{typenames}">{spatial_filter}</wfs:Query></wfs:GetFeature>'
|
||||||
return _filter
|
return _filter
|
||||||
|
|
||||||
def get_features(self,
|
def get_features(self,
|
||||||
@ -140,7 +139,7 @@ class ParcelWFSFetcher(AbstractWFSFetcher):
|
|||||||
Returns:
|
Returns:
|
||||||
features (list): A list of returned features
|
features (list): A list of returned features
|
||||||
"""
|
"""
|
||||||
found_features = []
|
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,
|
||||||
@ -156,11 +155,19 @@ class ParcelWFSFetcher(AbstractWFSFetcher):
|
|||||||
)
|
)
|
||||||
|
|
||||||
content = response.content.decode("utf-8")
|
content = response.content.decode("utf-8")
|
||||||
try:
|
content = xmltodict.parse(content)
|
||||||
# Check if collection is an exception and does not contain the requested data
|
collection = content.get(
|
||||||
content = json.loads(content)
|
"wfs:FeatureCollection",
|
||||||
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(
|
||||||
@ -170,21 +177,22 @@ class ParcelWFSFetcher(AbstractWFSFetcher):
|
|||||||
start_index,
|
start_index,
|
||||||
rerun_on_exception=False
|
rerun_on_exception=False
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
e.msg += content
|
members = collection.get(
|
||||||
raise e
|
"wfs:member",
|
||||||
fetched_features = content.get(
|
None,
|
||||||
"features",
|
|
||||||
{},
|
|
||||||
)
|
)
|
||||||
|
if members is not None:
|
||||||
|
if len(members) > 1:
|
||||||
|
# extend feature list with found list of new feature members
|
||||||
|
features += members
|
||||||
|
else:
|
||||||
|
# convert single found feature member into list and extent feature list
|
||||||
|
features += [members]
|
||||||
|
|
||||||
found_features += fetched_features
|
if collection.get("@next", None) is not None:
|
||||||
|
|
||||||
if len(fetched_features) < self.count:
|
|
||||||
# The response was not 'full', so we got everything to fetch
|
|
||||||
start_index = None
|
|
||||||
else:
|
|
||||||
# If a 'full' response returned, there might be more to fetch. Increase the start_index!
|
|
||||||
start_index += self.count
|
start_index += self.count
|
||||||
|
else:
|
||||||
|
start_index = None
|
||||||
|
|
||||||
return found_features
|
return features
|
||||||
|
@ -5,12 +5,9 @@ Contact: michel.peltriaux@sgdnord.rlp.de
|
|||||||
Created on: 16.11.20
|
Created on: 16.11.20
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import json
|
|
||||||
|
|
||||||
import requests
|
|
||||||
from django.contrib.auth import logout
|
from django.contrib.auth import logout
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.http import HttpRequest, HttpResponse, JsonResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
from django.shortcuts import redirect, render, get_object_or_404
|
from django.shortcuts import redirect, render, get_object_or_404
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
@ -20,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, Municipal
|
from konova.models import Deadline, Geometry
|
||||||
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
|
||||||
@ -113,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/parcels/parcel_table_frame.html"
|
template = "konova/includes/parcel_table.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
|
||||||
@ -133,23 +130,8 @@ 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
|
|
||||||
num_all_parcels = parcels.count()
|
|
||||||
parcels = parcels[:rpp]
|
|
||||||
next_page = 1
|
|
||||||
if len(parcels) < rpp:
|
|
||||||
next_page = None
|
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
"num_parcels": num_all_parcels,
|
|
||||||
"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)
|
||||||
@ -157,49 +139,6 @@ 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
|
||||||
|
|
||||||
@ -225,26 +164,3 @@ def get_500_view(request: HttpRequest):
|
|||||||
"""
|
"""
|
||||||
context = BaseContext.context
|
context = BaseContext.context
|
||||||
return render(request, "500.html", context, status=500)
|
return render(request, "500.html", context, status=500)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def map_client_proxy_view(request: HttpRequest):
|
|
||||||
""" Provides proxy functionality for NETGIS map client.
|
|
||||||
|
|
||||||
Used for fetching content of a provided url
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
url = request.META.get("QUERY_STRING")
|
|
||||||
response = requests.get(url)
|
|
||||||
body = json.loads(response.content)
|
|
||||||
if response.status_code != 200:
|
|
||||||
return JsonResponse({
|
|
||||||
"status_code": response.status_code,
|
|
||||||
"content": body,
|
|
||||||
})
|
|
||||||
return JsonResponse(body)
|
|
||||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -28,7 +28,6 @@ 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
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
{% 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>
|
|
@ -11,7 +11,7 @@
|
|||||||
</h4>
|
</h4>
|
||||||
{% if form.form_caption is not None %}
|
{% if form.form_caption is not None %}
|
||||||
<small>
|
<small>
|
||||||
{{ form.form_caption|linebreaks }}
|
{{ form.form_caption }}
|
||||||
</small>
|
</small>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<form method="post" action="{{ form.action_url }}" {% for attr_key, attr_val in form.form_attrs.items %} {{attr_key}}="{{attr_val}}"{% endfor %}>
|
<form method="post" action="{{ form.action_url }}" {% for attr_key, attr_val in form.form_attrs.items %} {{attr_key}}="{{attr_val}}"{% endfor %}>
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 4.0 KiB |
@ -1,139 +0,0 @@
|
|||||||
{
|
|
||||||
"layers":
|
|
||||||
[
|
|
||||||
{ "folder": 5, "type": "WMS", "title": "KOM Flächen", "url": "https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/mod_ogc/wms_getmap.php?mapfile=kom_f&", "name": "kom_f" },
|
|
||||||
{ "folder": 5, "type": "WMS", "title": "KOM Linien", "url": "https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/mod_ogc/wms_getmap.php?mapfile=kom_l&", "name": "kom_l" },
|
|
||||||
{ "folder": 5, "type": "WMS", "title": "KOM Punkte", "url": "https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/mod_ogc/wms_getmap.php?mapfile=komon&", "name": "kom_p" },
|
|
||||||
|
|
||||||
{ "folder": 6, "type": "WMS", "title": "EIV Flächen", "url": "https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/mod_ogc/wms_getmap.php?mapfile=eiv_f&", "name": "eiv_f" },
|
|
||||||
{ "folder": 6, "type": "WMS", "title": "EIV Linien", "url": "https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/mod_ogc/wms_getmap.php?mapfile=eiv_l&", "name": "eiv_l" },
|
|
||||||
{ "folder": 6, "type": "WMS", "title": "EIV Punkte", "url": "https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/mod_ogc/wms_getmap.php?mapfile=eiv_p&", "name": "eiv_p" },
|
|
||||||
|
|
||||||
{ "folder": 7, "type": "WMS", "title": "OEK Flächen", "url": "https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/mod_ogc/wms_getmap.php?mapfile=oek_f&", "name": "oek_f" },
|
|
||||||
|
|
||||||
{ "folder": 8, "type": "WMS", "title": "EMA Flächen", "url": "https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/mod_ogc/wms_getmap.php?mapfile=ema_f&", "name": "ema_f" },
|
|
||||||
|
|
||||||
{ "folder": 9, "type": "WMS", "title": "MAE Flächen", "url": "https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/mod_ogc/wms_getmap.php?mapfile=mae&", "name": "mae" },
|
|
||||||
|
|
||||||
{ "folder": 10, "type": "WMS", "title": "Naturschutzgebiete", "url": "https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/mod_ogc/wms_getmap.php?mapfile=naturschutzgebiet&", "name": "naturschutzgebiet" },
|
|
||||||
{ "folder": 10, "type": "WMS", "title": "Naturparkzonen", "url": "https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/mod_ogc/wms_getmap.php?mapfile=naturparkzonen&", "name": "naturparkzonen" },
|
|
||||||
{ "folder": 10, "type": "WMS", "title": "Landschaftsschutzgebiete", "url": "https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/mod_ogc/wms_getmap.php?mapfile=landschaftsschutzgebiet&", "name": "landschaftsschutzgebiet" },
|
|
||||||
{ "folder": 10, "type": "WMS", "title": "Vogelschutzgebiete", "url": "https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/mod_ogc/wms_getmap.php?mapfile=vogelschutzgebiet&", "name": "vogelschutzgebiet" },
|
|
||||||
{ "folder": 10, "type": "WMS", "title": "FFH Gebiete", "url": "https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/mod_ogc/wms_getmap.php?mapfile=ffh&", "name": "ffh" },
|
|
||||||
|
|
||||||
{ "folder": 11, "type": "WMS", "title": "Nationalpark Zonen", "url": "https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/mod_ogc/wms_getmap.php?mapfile=nationalpark_zonen&", "name": "nationalpark_zonen" },
|
|
||||||
{ "folder": 11, "type": "WMS", "title": "Nationalpark Grenzen", "url": "https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/mod_ogc/wms_getmap.php?mapfile=nationalpark_grenze&", "name": "nationalpark_grenze" },
|
|
||||||
|
|
||||||
{ "folder": 12, "type": "WMS", "title": "Naturräume Grenzen", "url": "https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/mod_ogc/wms_getmap.php?mapfile=natraum_lkompvo_grenzen&", "name": "natraum_lkompvo_grenzen" },
|
|
||||||
{ "folder": 12, "type": "WMS", "title": "Naturräume Flächen", "url": "https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/mod_ogc/wms_getmap.php?mapfile=natraum_lkompvo&", "name": "natraum_lkompvo" },
|
|
||||||
|
|
||||||
{ "folder": 1, "type": "WMS", "title": "Lagebezeichnungen", "url": "https://geo5.service24.rlp.de/wms/liegenschaften_rp.fcgi?", "name": "Lagebezeichnungen" },
|
|
||||||
{ "folder": 1, "type": "WMS", "title": "Flurstücke", "url": "https://geo5.service24.rlp.de/wms/liegenschaften_rp.fcgi?", "name": "Flurstueck", "active": true},
|
|
||||||
{ "folder": 1, "type": "WMS", "title": "Gebäude / Bauwerke", "url": "https://geo5.service24.rlp.de/wms/liegenschaften_rp.fcgi?", "name": "GebaeudeBauwerke" },
|
|
||||||
{ "folder": 1, "type": "WMS", "title": "Nutzung", "url": "https://geo5.service24.rlp.de/wms/liegenschaften_rp.fcgi?", "name": "Nutzung" },
|
|
||||||
|
|
||||||
{ "folder": 2, "type": "WMS", "title": "Landkreise", "url": "http://geo5.service24.rlp.de/wms/verwaltungsgrenzen_rp.fcgi?", "name": "Landkreise" },
|
|
||||||
{ "folder": 2, "type": "WMS", "title": "Verbandsgemeinden", "url": "http://geo5.service24.rlp.de/wms/verwaltungsgrenzen_rp.fcgi?", "name": "Verbandsgemeinden" },
|
|
||||||
{ "folder": 2, "type": "WMS", "title": "Gemeinden", "url": "http://geo5.service24.rlp.de/wms/verwaltungsgrenzen_rp.fcgi?", "name": "Gemeinden" },
|
|
||||||
|
|
||||||
{ "folder": 0, "type": "WMS", "title": "Webatlas farbig", "attribution": "LVermGeo", "url": "https://maps.service24.rlp.de/gisserver/services/RP/RP_WebAtlasRP/MapServer/WmsServer?", "name": "RP_WebAtlasRP", "active": true },
|
|
||||||
{ "folder": 0, "type": "WMS", "title": "Webatlas grau", "attribution": "LVermGeo", "url": "https://maps.service24.rlp.de/gisserver/services/RP/RP_ETRS_Gt/MapServer/WmsServer?", "name": "0", "active": false },
|
|
||||||
{ "folder": 0, "type": "WMS", "title": "Luftbilder", "attribution": "LVermGeo", "url": "http://geo4.service24.rlp.de/wms/dop_basis.fcgi?", "name": "rp_dop", "active": false },
|
|
||||||
{ "folder": 0, "type": "WMS", "title": "TopPlusOpen", "attribution": "BKG", "url": "https://sgx.geodatenzentrum.de/wms_topplus_open?", "name": "web", "active": false },
|
|
||||||
{ "folder": 0, "type": "OSM", "title": "Open Street Map", "attribution": "OSM", "active": false }
|
|
||||||
],
|
|
||||||
|
|
||||||
"folders":
|
|
||||||
[
|
|
||||||
{ "title": "Hintergrund", "parent": -1 },
|
|
||||||
{ "title": "ALKIS Liegenschaften", "parent": -1 },
|
|
||||||
{ "title": "Verwaltungsgrenzen", "parent": -1 },
|
|
||||||
{ "title": "Geofachdaten", "parent": -1 },
|
|
||||||
{ "title": "Kompensationsverzeichnis", "parent": 3 },
|
|
||||||
{ "title": "Kompensationen", "parent": 4 },
|
|
||||||
{ "title": "Eingriffe", "parent": 4 },
|
|
||||||
{ "title": "Ökokonten", "parent": 4 },
|
|
||||||
{ "title": "EMA", "parent": 4 },
|
|
||||||
{ "title": "MAE", "parent": 4 },
|
|
||||||
{ "title": "Schutzgebiete", "parent": 3 },
|
|
||||||
{ "title": "Nationalparke", "parent": 10 },
|
|
||||||
{ "title": "Naturräume", "parent": 10 }
|
|
||||||
],
|
|
||||||
|
|
||||||
"projections":
|
|
||||||
[
|
|
||||||
[ "EPSG:25832", "+proj=utm +zone=32 +ellps=GRS80 +units=m +no_defs" ]
|
|
||||||
],
|
|
||||||
|
|
||||||
"map":
|
|
||||||
{
|
|
||||||
"projection": "EPSG:25832",
|
|
||||||
"center": [ 385000, 5543000 ],
|
|
||||||
"minZoom": 5,
|
|
||||||
"maxZoom": 22,
|
|
||||||
"zoom": 9,
|
|
||||||
"attribution": "LANIS RLP"
|
|
||||||
},
|
|
||||||
|
|
||||||
"output":
|
|
||||||
{
|
|
||||||
"id": "netgis-storage"
|
|
||||||
},
|
|
||||||
|
|
||||||
"search":
|
|
||||||
{
|
|
||||||
"url": "/client/proxy?https://www.geoportal.rlp.de/mapbender/geoportal/gaz_geom_mobile.php?outputFormat=json&resultTarget=web&searchEPSG={epsg}&maxResults=5&maxRows=5&featureClass=P&style=full&searchText={q}&name_startsWith={q}"
|
|
||||||
},
|
|
||||||
|
|
||||||
"export":
|
|
||||||
{
|
|
||||||
"logo": "/static/assets/logo.png",
|
|
||||||
"gifWebWorker": "/static/libs/gifjs/0.2.0/gif.worker.js",
|
|
||||||
"defaultFilename": "Export",
|
|
||||||
"defaultMargin": 10
|
|
||||||
},
|
|
||||||
|
|
||||||
"tools":
|
|
||||||
{
|
|
||||||
"buffer":
|
|
||||||
{
|
|
||||||
"defaultRadius": 2,
|
|
||||||
"defaultSegments": 2
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"styles":
|
|
||||||
{
|
|
||||||
"editLayer":
|
|
||||||
{
|
|
||||||
"fill": "rgba( 255, 0, 0, 0.2 )",
|
|
||||||
"stroke": "#ff0000",
|
|
||||||
"strokeWidth": 3,
|
|
||||||
"pointRadius": 6
|
|
||||||
},
|
|
||||||
|
|
||||||
"select":
|
|
||||||
{
|
|
||||||
"fill": "rgba( 0, 127, 255, 0.5 )",
|
|
||||||
"stroke": "#007fff",
|
|
||||||
"strokeWidth": 3,
|
|
||||||
"pointRadius": 6
|
|
||||||
},
|
|
||||||
|
|
||||||
"sketch":
|
|
||||||
{
|
|
||||||
"fill": "rgba( 0, 127, 255, 0.2 )",
|
|
||||||
"stroke": "#0080ff",
|
|
||||||
"strokeWidth": 3,
|
|
||||||
"pointRadius": 6
|
|
||||||
},
|
|
||||||
|
|
||||||
"modify":
|
|
||||||
{
|
|
||||||
"fill": "rgba( 0, 127, 255, 0.5 )",
|
|
||||||
"stroke": "#0080ff",
|
|
||||||
"strokeWidth": 3,
|
|
||||||
"pointRadius": 6
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
{% load static %}
|
|
||||||
<!-- Library Styles -->
|
|
||||||
<link rel="stylesheet" type="text/css" href="{% static 'fontawesome/5.12.0/css/all.min.css' %}" />
|
|
||||||
<link rel="stylesheet" type="text/css" href="{% static 'openlayers/6.14.1/ol.css' %}" />
|
|
||||||
|
|
||||||
<!-- Client Styles -->
|
|
||||||
<link rel="stylesheet" type="text/css" href="{% static 'netgis.min.css' %}" />
|
|
||||||
|
|
||||||
<main id="container" {% if geom_form.read_only %}data-editable="false"{% else %}data-editable="true"{% endif %} style="position: relative; width: 100%; height: 100%;">
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<!--<main id="container" contenteditable="false" style="position: absolute; width: 100%; height: 100%; left: 0mm; top: 0mm;">
|
|
||||||
</main>-->
|
|
||||||
|
|
||||||
<input type="hidden" id="netgis-storage" name="geom" value="{{geom_form.fields.geom.initial}}"/>
|
|
||||||
|
|
||||||
<!-- Library Scripts -->
|
|
||||||
<script type="text/javascript" src="{% static 'openlayers/6.14.1/ol.js' %}"></script>
|
|
||||||
<script type="text/javascript" src="{% static 'proj4js/2.6.0/proj4.js' %}"></script>
|
|
||||||
<script type="text/javascript" src="{% static 'jsts/1.6.1/jsts.min.js' %}"></script>
|
|
||||||
<script type="text/javascript" src="{% static 'shapefilejs/4.0.2/shp.js' %}"></script>
|
|
||||||
<script type="text/javascript" src="{% static 'jspdf/1.3.2/jspdf.min.js' %}"></script>
|
|
||||||
<script type="text/javascript" src="{% static 'gifjs/0.2.0/gif.js' %}"></script>
|
|
||||||
|
|
||||||
<!-- Client Scripts -->
|
|
||||||
<script type="text/javascript" src="{% static 'netgis.min.js' %}"></script>
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
|
|
||||||
// Create Client Instance
|
|
||||||
var client = new netgis.Client( "container", "{% static 'config.json' %}" );
|
|
||||||
|
|
||||||
</script>
|
|
@ -1,34 +0,0 @@
|
|||||||
Font Awesome Free License
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
Font Awesome Free is free, open source, and GPL friendly. You can use it for
|
|
||||||
commercial projects, open source projects, or really almost whatever you want.
|
|
||||||
Full Font Awesome Free license: https://fontawesome.com/license/free.
|
|
||||||
|
|
||||||
# Icons: CC BY 4.0 License (https://creativecommons.org/licenses/by/4.0/)
|
|
||||||
In the Font Awesome Free download, the CC BY 4.0 license applies to all icons
|
|
||||||
packaged as SVG and JS file types.
|
|
||||||
|
|
||||||
# Fonts: SIL OFL 1.1 License (https://scripts.sil.org/OFL)
|
|
||||||
In the Font Awesome Free download, the SIL OFL license applies to all icons
|
|
||||||
packaged as web and desktop font files.
|
|
||||||
|
|
||||||
# Code: MIT License (https://opensource.org/licenses/MIT)
|
|
||||||
In the Font Awesome Free download, the MIT license applies to all non-font and
|
|
||||||
non-icon files.
|
|
||||||
|
|
||||||
# Attribution
|
|
||||||
Attribution is required by MIT, SIL OFL, and CC BY licenses. Downloaded Font
|
|
||||||
Awesome Free files already contain embedded comments with sufficient
|
|
||||||
attribution, so you shouldn't need to do anything additional when using these
|
|
||||||
files normally.
|
|
||||||
|
|
||||||
We've kept attribution comments terse, so we ask that you do not actively work
|
|
||||||
to remove them from files, especially code. They're a great way for folks to
|
|
||||||
learn about Font Awesome.
|
|
||||||
|
|
||||||
# Brand Icons
|
|
||||||
All brand icons are trademarks of their respective owners. The use of these
|
|
||||||
trademarks does not indicate endorsement of the trademark holder by Font
|
|
||||||
Awesome, nor vice versa. **Please do not use brand logos for any purpose except
|
|
||||||
to represent the company, product, or service to which they refer.**
|
|
4450
templates/map/client/libs/fontawesome/5.12.0/css/all.css
vendored
4450
templates/map/client/libs/fontawesome/5.12.0/css/all.css
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@ -1,14 +0,0 @@
|
|||||||
/*!
|
|
||||||
* Font Awesome Free 5.12.0 by @fontawesome - https://fontawesome.com
|
|
||||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
|
||||||
*/
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Font Awesome 5 Brands';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: normal;
|
|
||||||
font-display: auto;
|
|
||||||
src: url("../webfonts/fa-brands-400.eot");
|
|
||||||
src: url("../webfonts/fa-brands-400.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.woff") format("woff"), url("../webfonts/fa-brands-400.ttf") format("truetype"), url("../webfonts/fa-brands-400.svg#fontawesome") format("svg"); }
|
|
||||||
|
|
||||||
.fab {
|
|
||||||
font-family: 'Font Awesome 5 Brands'; }
|
|
@ -1,5 +0,0 @@
|
|||||||
/*!
|
|
||||||
* Font Awesome Free 5.12.0 by @fontawesome - https://fontawesome.com
|
|
||||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
|
||||||
*/
|
|
||||||
@font-face{font-family:"Font Awesome 5 Brands";font-style:normal;font-weight:normal;font-display:auto;src:url(../webfonts/fa-brands-400.eot);src:url(../webfonts/fa-brands-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.woff) format("woff"),url(../webfonts/fa-brands-400.ttf) format("truetype"),url(../webfonts/fa-brands-400.svg#fontawesome) format("svg")}.fab{font-family:"Font Awesome 5 Brands"}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user