Merge pull request '49_Parcel_calculation' (#56) from 49_Parcel_calculation into master
Reviewed-on: SGD-Nord/konova#56
This commit is contained in:
commit
193995c625
@ -64,8 +64,8 @@ class TimespanReport:
|
|||||||
responsible__conservation_office__id=id,
|
responsible__conservation_office__id=id,
|
||||||
legal__registration_date__gt=LKOMPVZVO_PUBLISH_DATE,
|
legal__registration_date__gt=LKOMPVZVO_PUBLISH_DATE,
|
||||||
deleted=None,
|
deleted=None,
|
||||||
created__timestamp__gte=date_from,
|
created__timestamp__date__gte=date_from,
|
||||||
created__timestamp__lte=date_to,
|
created__timestamp__date__lte=date_to,
|
||||||
)
|
)
|
||||||
self.queryset_checked = self.queryset.filter(
|
self.queryset_checked = self.queryset.filter(
|
||||||
checked__isnull=False
|
checked__isnull=False
|
||||||
@ -231,8 +231,8 @@ class TimespanReport:
|
|||||||
intervention__responsible__conservation_office__id=id,
|
intervention__responsible__conservation_office__id=id,
|
||||||
intervention__legal__registration_date__gt=LKOMPVZVO_PUBLISH_DATE,
|
intervention__legal__registration_date__gt=LKOMPVZVO_PUBLISH_DATE,
|
||||||
deleted=None,
|
deleted=None,
|
||||||
intervention__created__timestamp__gte=date_from,
|
intervention__created__date__timestamp__gte=date_from,
|
||||||
intervention__created__timestamp__lte=date_to,
|
intervention__created__date__timestamp__lte=date_to,
|
||||||
)
|
)
|
||||||
self.queryset_checked = self.queryset.filter(
|
self.queryset_checked = self.queryset.filter(
|
||||||
intervention__checked__isnull=False
|
intervention__checked__isnull=False
|
||||||
@ -400,8 +400,8 @@ class TimespanReport:
|
|||||||
self.queryset = EcoAccount.objects.filter(
|
self.queryset = EcoAccount.objects.filter(
|
||||||
responsible__conservation_office__id=id,
|
responsible__conservation_office__id=id,
|
||||||
deleted=None,
|
deleted=None,
|
||||||
created__timestamp__gte=date_from,
|
created__timestamp__date__gte=date_from,
|
||||||
created__timestamp__lte=date_to,
|
created__timestamp__date__lte=date_to,
|
||||||
)
|
)
|
||||||
self.queryset_recorded = self.queryset.filter(
|
self.queryset_recorded = self.queryset.filter(
|
||||||
recorded__isnull=False
|
recorded__isnull=False
|
||||||
@ -479,8 +479,8 @@ class TimespanReport:
|
|||||||
legal__registration_date__lte=LKOMPVZVO_PUBLISH_DATE,
|
legal__registration_date__lte=LKOMPVZVO_PUBLISH_DATE,
|
||||||
responsible__conservation_office__id=id,
|
responsible__conservation_office__id=id,
|
||||||
deleted=None,
|
deleted=None,
|
||||||
created__timestamp__gte=date_from,
|
created__timestamp__date__gte=date_from,
|
||||||
created__timestamp__lte=date_to,
|
created__timestamp__date__lte=date_to,
|
||||||
)
|
)
|
||||||
self.queryset_intervention_recorded = self.queryset_intervention.filter(
|
self.queryset_intervention_recorded = self.queryset_intervention.filter(
|
||||||
recorded__isnull=False
|
recorded__isnull=False
|
||||||
|
@ -170,7 +170,7 @@ class AbstractCompensation(BaseObject, GeoReferencedMixin):
|
|||||||
"""
|
"""
|
||||||
if not self.is_shared_with(request.user):
|
if not self.is_shared_with(request.user):
|
||||||
messages.info(request, DATA_UNSHARED_EXPLANATION)
|
messages.info(request, DATA_UNSHARED_EXPLANATION)
|
||||||
request = self._set_geometry_conflict_message(request)
|
request = self.set_geometry_conflict_message(request)
|
||||||
return request
|
return request
|
||||||
|
|
||||||
|
|
||||||
|
@ -110,7 +110,10 @@
|
|||||||
{% include 'map/geom_form.html' %}
|
{% include 'map/geom_form.html' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% include 'konova/comment_card.html' %}
|
{% include 'konova/includes/parcels.html' %}
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
{% include 'konova/includes/comment_card.html' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -93,7 +93,10 @@
|
|||||||
{% include 'map/geom_form.html' %}
|
{% include 'map/geom_form.html' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% include 'konova/comment_card.html' %}
|
{% include 'konova/includes/parcels.html' %}
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
{% include 'konova/includes/comment_card.html' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -37,6 +37,9 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
{% include 'map/geom_form.html' %}
|
{% include 'map/geom_form.html' %}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
{% include 'konova/includes/parcels.html' %}
|
||||||
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-6 col-md-6 col-lg-6">
|
<div class="col-sm-6 col-md-6 col-lg-6">
|
||||||
<h4>{% trans 'Open in browser' %}</h4>
|
<h4>{% trans 'Open in browser' %}</h4>
|
||||||
|
@ -54,6 +54,9 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
{% include 'map/geom_form.html' %}
|
{% include 'map/geom_form.html' %}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
{% include 'konova/includes/parcels.html' %}
|
||||||
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-6 col-md-6 col-lg-6">
|
<div class="col-sm-6 col-md-6 col-lg-6">
|
||||||
<h4>{% trans 'Open in browser' %}</h4>
|
<h4>{% trans 'Open in browser' %}</h4>
|
||||||
|
@ -170,6 +170,7 @@ def detail_view(request: HttpRequest, id: str):
|
|||||||
template = "compensation/detail/compensation/view.html"
|
template = "compensation/detail/compensation/view.html"
|
||||||
comp = get_object_or_404(Compensation, id=id)
|
comp = get_object_or_404(Compensation, id=id)
|
||||||
geom_form = SimpleGeomForm(instance=comp)
|
geom_form = SimpleGeomForm(instance=comp)
|
||||||
|
parcels = comp.get_underlying_parcels()
|
||||||
_user = request.user
|
_user = request.user
|
||||||
is_data_shared = comp.intervention.is_shared_with(_user)
|
is_data_shared = comp.intervention.is_shared_with(_user)
|
||||||
|
|
||||||
@ -189,6 +190,7 @@ def detail_view(request: HttpRequest, id: str):
|
|||||||
context = {
|
context = {
|
||||||
"obj": comp,
|
"obj": comp,
|
||||||
"geom_form": geom_form,
|
"geom_form": geom_form,
|
||||||
|
"parcels": parcels,
|
||||||
"has_access": is_data_shared,
|
"has_access": is_data_shared,
|
||||||
"actions": actions,
|
"actions": actions,
|
||||||
"before_states": before_states,
|
"before_states": before_states,
|
||||||
@ -451,6 +453,7 @@ def report_view(request: HttpRequest, id: str):
|
|||||||
geom_form = SimpleGeomForm(
|
geom_form = SimpleGeomForm(
|
||||||
instance=comp
|
instance=comp
|
||||||
)
|
)
|
||||||
|
parcels = comp.get_underlying_parcels()
|
||||||
qrcode_img = generate_qr_code(
|
qrcode_img = generate_qr_code(
|
||||||
request.build_absolute_uri(reverse("compensation:report", args=(id,))),
|
request.build_absolute_uri(reverse("compensation:report", args=(id,))),
|
||||||
10
|
10
|
||||||
@ -472,6 +475,7 @@ def report_view(request: HttpRequest, id: str):
|
|||||||
"before_states": before_states,
|
"before_states": before_states,
|
||||||
"after_states": after_states,
|
"after_states": after_states,
|
||||||
"geom_form": geom_form,
|
"geom_form": geom_form,
|
||||||
|
"parcels": parcels,
|
||||||
"actions": actions,
|
"actions": actions,
|
||||||
}
|
}
|
||||||
context = BaseContext(request, context).context
|
context = BaseContext(request, context).context
|
||||||
|
@ -181,6 +181,7 @@ def detail_view(request: HttpRequest, id: str):
|
|||||||
id=id
|
id=id
|
||||||
)
|
)
|
||||||
geom_form = SimpleGeomForm(instance=acc)
|
geom_form = SimpleGeomForm(instance=acc)
|
||||||
|
parcels = acc.get_underlying_parcels()
|
||||||
_user = request.user
|
_user = request.user
|
||||||
is_data_shared = acc.is_shared_with(_user)
|
is_data_shared = acc.is_shared_with(_user)
|
||||||
|
|
||||||
@ -207,6 +208,7 @@ def detail_view(request: HttpRequest, id: str):
|
|||||||
context = {
|
context = {
|
||||||
"obj": acc,
|
"obj": acc,
|
||||||
"geom_form": geom_form,
|
"geom_form": geom_form,
|
||||||
|
"parcels": parcels,
|
||||||
"has_access": is_data_shared,
|
"has_access": is_data_shared,
|
||||||
"before_states": before_states,
|
"before_states": before_states,
|
||||||
"after_states": after_states,
|
"after_states": after_states,
|
||||||
@ -553,6 +555,7 @@ def report_view(request:HttpRequest, id: str):
|
|||||||
geom_form = SimpleGeomForm(
|
geom_form = SimpleGeomForm(
|
||||||
instance=acc
|
instance=acc
|
||||||
)
|
)
|
||||||
|
parcels = acc.get_underlying_parcels()
|
||||||
qrcode_img = generate_qr_code(
|
qrcode_img = generate_qr_code(
|
||||||
request.build_absolute_uri(reverse("ema:report", args=(id,))),
|
request.build_absolute_uri(reverse("ema:report", args=(id,))),
|
||||||
10
|
10
|
||||||
@ -580,6 +583,7 @@ def report_view(request:HttpRequest, id: str):
|
|||||||
"before_states": before_states,
|
"before_states": before_states,
|
||||||
"after_states": after_states,
|
"after_states": after_states,
|
||||||
"geom_form": geom_form,
|
"geom_form": geom_form,
|
||||||
|
"parcels": parcels,
|
||||||
"actions": actions,
|
"actions": actions,
|
||||||
"deductions": deductions,
|
"deductions": deductions,
|
||||||
}
|
}
|
||||||
|
@ -106,7 +106,7 @@ class Ema(AbstractCompensation, ShareableObjectMixin, RecordableObjectMixin):
|
|||||||
"""
|
"""
|
||||||
if not self.is_shared_with(request.user):
|
if not self.is_shared_with(request.user):
|
||||||
messages.info(request, DATA_UNSHARED_EXPLANATION)
|
messages.info(request, DATA_UNSHARED_EXPLANATION)
|
||||||
self._set_geometry_conflict_message(request)
|
self.set_geometry_conflict_message(request)
|
||||||
return request
|
return request
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,7 +2,13 @@
|
|||||||
{% load i18n l10n static fontawesome_5 humanize %}
|
{% load i18n l10n static fontawesome_5 humanize %}
|
||||||
|
|
||||||
{% block head %}
|
{% block head %}
|
||||||
|
{% comment %}
|
||||||
|
dal documentation (django-autocomplete-light) states using form.media for adding needed scripts.
|
||||||
|
This does not work properly with modal forms, as the scripts are not loaded properly inside the modal.
|
||||||
|
Therefore the script linkages from form.media have been extracted and put inside dal/scripts.html to ensure
|
||||||
|
these scripts are loaded when needed.
|
||||||
|
{% endcomment %}
|
||||||
|
{% include 'dal/scripts.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
@ -77,7 +83,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-12 col-md-12 col-lg-6">
|
<div class="col-sm-12 col-md-12 col-lg-6">
|
||||||
|
<div class="row">
|
||||||
{% include 'map/geom_form.html' %}
|
{% include 'map/geom_form.html' %}
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
{% include 'konova/includes/parcels.html' %}
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
{% include 'konova/includes/comment_card.html' %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
|
@ -41,6 +41,9 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
{% include 'map/geom_form.html' %}
|
{% include 'map/geom_form.html' %}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
{% include 'konova/includes/parcels.html' %}
|
||||||
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-6 col-md-6 col-lg-6">
|
<div class="col-sm-6 col-md-6 col-lg-6">
|
||||||
<h4>{% trans 'Open in browser' %}</h4>
|
<h4>{% trans 'Open in browser' %}</h4>
|
||||||
|
@ -125,6 +125,7 @@ def detail_view(request: HttpRequest, id: str):
|
|||||||
ema = get_object_or_404(Ema, id=id, deleted=None)
|
ema = get_object_or_404(Ema, id=id, deleted=None)
|
||||||
|
|
||||||
geom_form = SimpleGeomForm(instance=ema)
|
geom_form = SimpleGeomForm(instance=ema)
|
||||||
|
parcels = ema.get_underlying_parcels()
|
||||||
_user = request.user
|
_user = request.user
|
||||||
is_data_shared = ema.is_shared_with(_user)
|
is_data_shared = ema.is_shared_with(_user)
|
||||||
|
|
||||||
@ -143,6 +144,7 @@ def detail_view(request: HttpRequest, id: str):
|
|||||||
context = {
|
context = {
|
||||||
"obj": ema,
|
"obj": ema,
|
||||||
"geom_form": geom_form,
|
"geom_form": geom_form,
|
||||||
|
"parcels": parcels,
|
||||||
"has_access": is_data_shared,
|
"has_access": is_data_shared,
|
||||||
"before_states": before_states,
|
"before_states": before_states,
|
||||||
"after_states": after_states,
|
"after_states": after_states,
|
||||||
@ -464,6 +466,7 @@ def report_view(request:HttpRequest, id: str):
|
|||||||
geom_form = SimpleGeomForm(
|
geom_form = SimpleGeomForm(
|
||||||
instance=ema,
|
instance=ema,
|
||||||
)
|
)
|
||||||
|
parcels = ema.get_underlying_parcels()
|
||||||
qrcode_img = generate_qr_code(
|
qrcode_img = generate_qr_code(
|
||||||
request.build_absolute_uri(reverse("ema:report", args=(id,))),
|
request.build_absolute_uri(reverse("ema:report", args=(id,))),
|
||||||
10
|
10
|
||||||
@ -485,6 +488,7 @@ def report_view(request:HttpRequest, id: str):
|
|||||||
"before_states": before_states,
|
"before_states": before_states,
|
||||||
"after_states": after_states,
|
"after_states": after_states,
|
||||||
"geom_form": geom_form,
|
"geom_form": geom_form,
|
||||||
|
"parcels": parcels,
|
||||||
"actions": actions,
|
"actions": actions,
|
||||||
}
|
}
|
||||||
context = BaseContext(request, context).context
|
context = BaseContext(request, context).context
|
||||||
|
@ -278,7 +278,7 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec
|
|||||||
"""
|
"""
|
||||||
if not self.is_shared_with(request.user):
|
if not self.is_shared_with(request.user):
|
||||||
messages.info(request, DATA_UNSHARED_EXPLANATION)
|
messages.info(request, DATA_UNSHARED_EXPLANATION)
|
||||||
request = self._set_geometry_conflict_message(request)
|
request = self.set_geometry_conflict_message(request)
|
||||||
return request
|
return request
|
||||||
|
|
||||||
|
|
||||||
|
@ -127,7 +127,10 @@
|
|||||||
{% include 'map/geom_form.html' %}
|
{% include 'map/geom_form.html' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% include 'konova/comment_card.html' %}
|
{% include 'konova/includes/parcels.html' %}
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
{% include 'konova/includes/comment_card.html' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -100,6 +100,9 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
{% include 'map/geom_form.html' %}
|
{% include 'map/geom_form.html' %}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
{% include 'konova/includes/parcels.html' %}
|
||||||
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-6 col-md-6 col-lg-6">
|
<div class="col-sm-6 col-md-6 col-lg-6">
|
||||||
<h4>{% trans 'Open in browser' %}</h4>
|
<h4>{% trans 'Open in browser' %}</h4>
|
||||||
|
@ -236,6 +236,8 @@ def detail_view(request: HttpRequest, id: str):
|
|||||||
instance=intervention,
|
instance=intervention,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
parcels = intervention.get_underlying_parcels()
|
||||||
|
|
||||||
# Inform user about revocation
|
# Inform user about revocation
|
||||||
if intervention.legal.revocations.exists():
|
if intervention.legal.revocations.exists():
|
||||||
messages.error(
|
messages.error(
|
||||||
@ -249,6 +251,7 @@ def detail_view(request: HttpRequest, id: str):
|
|||||||
"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),
|
||||||
@ -544,6 +547,7 @@ def report_view(request:HttpRequest, id: str):
|
|||||||
geom_form = SimpleGeomForm(
|
geom_form = SimpleGeomForm(
|
||||||
instance=intervention
|
instance=intervention
|
||||||
)
|
)
|
||||||
|
parcels = intervention.get_underlying_parcels()
|
||||||
|
|
||||||
distinct_deductions = intervention.deductions.all().distinct(
|
distinct_deductions = intervention.deductions.all().distinct(
|
||||||
"account"
|
"account"
|
||||||
@ -562,6 +566,7 @@ def report_view(request:HttpRequest, id: str):
|
|||||||
"qrcode": qrcode_img,
|
"qrcode": qrcode_img,
|
||||||
"qrcode_lanis": qrcode_img_lanis,
|
"qrcode_lanis": qrcode_img_lanis,
|
||||||
"geom_form": geom_form,
|
"geom_form": geom_form,
|
||||||
|
"parcels": parcels,
|
||||||
}
|
}
|
||||||
context = BaseContext(request, context).context
|
context = BaseContext(request, context).context
|
||||||
return render(request, template, context)
|
return render(request, template, context)
|
||||||
|
@ -7,7 +7,7 @@ Created on: 22.07.21
|
|||||||
"""
|
"""
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from konova.models import Geometry, Deadline, GeometryConflict
|
from konova.models import Geometry, Deadline, GeometryConflict, Parcel, District
|
||||||
|
|
||||||
|
|
||||||
class GeometryAdmin(admin.ModelAdmin):
|
class GeometryAdmin(admin.ModelAdmin):
|
||||||
@ -17,6 +17,25 @@ class GeometryAdmin(admin.ModelAdmin):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ParcelAdmin(admin.ModelAdmin):
|
||||||
|
list_display = [
|
||||||
|
"id",
|
||||||
|
"gmrkng",
|
||||||
|
"flr",
|
||||||
|
"flrstck_nnr",
|
||||||
|
"flrstck_zhlr",
|
||||||
|
"updated_on",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class DistrictAdmin(admin.ModelAdmin):
|
||||||
|
list_display = [
|
||||||
|
"id",
|
||||||
|
"gmnd",
|
||||||
|
"krs",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class GeometryConflictAdmin(admin.ModelAdmin):
|
class GeometryConflictAdmin(admin.ModelAdmin):
|
||||||
list_display = [
|
list_display = [
|
||||||
"conflicting_geometry",
|
"conflicting_geometry",
|
||||||
@ -52,5 +71,7 @@ class BaseObjectAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
|
|
||||||
admin.site.register(Geometry, GeometryAdmin)
|
admin.site.register(Geometry, GeometryAdmin)
|
||||||
|
admin.site.register(Parcel, ParcelAdmin)
|
||||||
|
admin.site.register(District, DistrictAdmin)
|
||||||
admin.site.register(GeometryConflict, GeometryConflictAdmin)
|
admin.site.register(GeometryConflict, GeometryConflictAdmin)
|
||||||
admin.site.register(Deadline, DeadlineAdmin)
|
admin.site.register(Deadline, DeadlineAdmin)
|
||||||
|
@ -15,18 +15,17 @@ class BaseContext:
|
|||||||
"""
|
"""
|
||||||
Holds all base data which is needed for every context rendering
|
Holds all base data which is needed for every context rendering
|
||||||
"""
|
"""
|
||||||
context = {
|
context = None
|
||||||
"base_title": BASE_TITLE,
|
|
||||||
"base_frontend_title": BASE_FRONTEND_TITLE,
|
|
||||||
"language": LANGUAGE_CODE,
|
|
||||||
"user": None,
|
|
||||||
"current_role": None,
|
|
||||||
"help_link": HELP_LINK,
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, request: HttpRequest, additional_context: dict = {}):
|
def __init__(self, request: HttpRequest, additional_context: dict = {}):
|
||||||
self.context["language"] = request.LANGUAGE_CODE
|
self.context = {
|
||||||
self.context["user"] = request.user
|
"base_title": BASE_TITLE,
|
||||||
|
"base_frontend_title": BASE_FRONTEND_TITLE,
|
||||||
|
"language": request.LANGUAGE_CODE,
|
||||||
|
"user": request.user,
|
||||||
|
"current_role": None,
|
||||||
|
"help_link": HELP_LINK
|
||||||
|
}
|
||||||
|
|
||||||
# Add additional context, derived from given parameters
|
# Add additional context, derived from given parameters
|
||||||
self.context.update(additional_context)
|
self.context.update(additional_context)
|
||||||
|
@ -287,6 +287,7 @@ class SimpleGeomForm(BaseForm):
|
|||||||
geometry = self.instance.geometry
|
geometry = self.instance.geometry
|
||||||
geometry.geom = self.cleaned_data.get("geom", MultiPolygon(srid=DEFAULT_SRID))
|
geometry.geom = self.cleaned_data.get("geom", MultiPolygon(srid=DEFAULT_SRID))
|
||||||
geometry.modified = action
|
geometry.modified = action
|
||||||
|
geometry.update_parcels()
|
||||||
geometry.save()
|
geometry.save()
|
||||||
except LookupError:
|
except LookupError:
|
||||||
# No geometry or linked instance holding a geometry exist --> create a new one!
|
# No geometry or linked instance holding a geometry exist --> create a new one!
|
||||||
|
@ -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
|
from konova.models import Deadline, Geometry, Parcel, District
|
||||||
from user.models import UserActionLogEntry
|
from user.models import UserActionLogEntry
|
||||||
|
|
||||||
|
|
||||||
@ -23,6 +23,7 @@ class Command(BaseKonovaCommand):
|
|||||||
self.sanitize_actions()
|
self.sanitize_actions()
|
||||||
self.sanitize_deadlines()
|
self.sanitize_deadlines()
|
||||||
self.sanitize_geometries()
|
self.sanitize_geometries()
|
||||||
|
self.sanitize_parcels_and_districts()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
self._break_line()
|
self._break_line()
|
||||||
exit(-1)
|
exit(-1)
|
||||||
@ -266,3 +267,34 @@ 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_parcels_and_districts(self):
|
||||||
|
""" Removes unattached parcels and districts
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
self._write_warning("=== Sanitize parcels and districts ===")
|
||||||
|
unrelated_parcels = Parcel.objects.filter(
|
||||||
|
geometries=None,
|
||||||
|
)
|
||||||
|
num_unrelated_parcels = unrelated_parcels.count()
|
||||||
|
if num_unrelated_parcels > 0:
|
||||||
|
self._write_error(f"Found {num_unrelated_parcels} unrelated parcel entries. Delete now...")
|
||||||
|
unrelated_parcels.delete()
|
||||||
|
self._write_success("Unrelated parcels deleted.")
|
||||||
|
else:
|
||||||
|
self._write_success("No unrelated parcels found.")
|
||||||
|
|
||||||
|
unrelated_districts = District.objects.filter(
|
||||||
|
parcels=None,
|
||||||
|
)
|
||||||
|
num_unrelated_districts = unrelated_districts.count()
|
||||||
|
if num_unrelated_districts > 0:
|
||||||
|
self._write_error(f"Found {num_unrelated_districts} unrelated district entries. Delete now...")
|
||||||
|
unrelated_districts.delete()
|
||||||
|
self._write_success("Unrelated districts deleted.")
|
||||||
|
else:
|
||||||
|
self._write_success("No unrelated districts found.")
|
||||||
|
|
||||||
|
self._break_line()
|
||||||
|
|
||||||
|
41
konova/management/commands/update_all_parcels.py
Normal file
41
konova/management/commands/update_all_parcels.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||||
|
Created on: 04.01.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from konova.management.commands.setup import BaseKonovaCommand
|
||||||
|
from konova.models import Geometry, Parcel, District
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseKonovaCommand):
|
||||||
|
help = "Checks the database' sanity and removes unused entries"
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
try:
|
||||||
|
self.update_all_parcels()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
self._break_line()
|
||||||
|
exit(-1)
|
||||||
|
|
||||||
|
def update_all_parcels(self):
|
||||||
|
num_parcels_before = Parcel.objects.count()
|
||||||
|
num_districts_before = District.objects.count()
|
||||||
|
self._write_warning("=== Update parcels and districts ===")
|
||||||
|
geometries = Geometry.objects.all().exclude(
|
||||||
|
geom=None
|
||||||
|
)
|
||||||
|
self._write_warning(f"Process parcels for {geometries.count()} geometry entries now ...")
|
||||||
|
for geometry in geometries:
|
||||||
|
geometry.update_parcels()
|
||||||
|
|
||||||
|
num_parcels_after = Parcel.objects.count()
|
||||||
|
num_districts_after = District.objects.count()
|
||||||
|
if num_parcels_after != num_parcels_before:
|
||||||
|
self._write_error(f"Parcels have changed: {num_parcels_before} to {num_parcels_after} entries. You should run the sanitize command.")
|
||||||
|
if num_districts_after != num_districts_before:
|
||||||
|
self._write_error(f"Districts have changed: {num_districts_before} to {num_districts_after} entries. You should run the sanitize command.")
|
||||||
|
|
||||||
|
self._write_success("Updating parcels done!")
|
||||||
|
self._break_line()
|
@ -9,3 +9,4 @@ from .object import *
|
|||||||
from .deadline import *
|
from .deadline import *
|
||||||
from .document import *
|
from .document import *
|
||||||
from .geometry import *
|
from .geometry import *
|
||||||
|
from .parcel import *
|
||||||
|
@ -7,9 +7,10 @@ Created on: 15.11.21
|
|||||||
"""
|
"""
|
||||||
from django.contrib.gis.db.models import MultiPolygonField
|
from django.contrib.gis.db.models import MultiPolygonField
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import Q
|
from django.utils import timezone
|
||||||
|
|
||||||
from konova.models import BaseResource, UuidModel
|
from konova.models import BaseResource, UuidModel
|
||||||
|
from konova.utils.wfs.spatial import ParcelWFSFetcher
|
||||||
|
|
||||||
|
|
||||||
class Geometry(BaseResource):
|
class Geometry(BaseResource):
|
||||||
@ -32,7 +33,7 @@ class Geometry(BaseResource):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
# If no geometry is given or important data is missing, we can not perform any checks
|
# If no geometry is given or important data is missing, we can not perform any checks
|
||||||
if self.geom is None or (self.created is None and self.modified is None):
|
if self.geom is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
self.recheck_existing_conflicts()
|
self.recheck_existing_conflicts()
|
||||||
@ -44,7 +45,10 @@ class Geometry(BaseResource):
|
|||||||
).distinct()
|
).distinct()
|
||||||
|
|
||||||
for match in overlapping_geoms:
|
for match in overlapping_geoms:
|
||||||
GeometryConflict.objects.get_or_create(conflicting_geometry=self, affected_geometry=match)
|
# Make sure this conflict is not already known but in a swapped constellation
|
||||||
|
conflict_exists_swapped = GeometryConflict.objects.filter(conflicting_geometry=match, affected_geometry=self).exists()
|
||||||
|
if not conflict_exists_swapped:
|
||||||
|
GeometryConflict.objects.get_or_create(conflicting_geometry=self, affected_geometry=match)
|
||||||
|
|
||||||
def recheck_existing_conflicts(self):
|
def recheck_existing_conflicts(self):
|
||||||
""" Rechecks GeometryConflict entries
|
""" Rechecks GeometryConflict entries
|
||||||
@ -69,7 +73,6 @@ class Geometry(BaseResource):
|
|||||||
resolved_conflicts = all_conflicted_by_conflicts.exclude(id__in=still_conflicting_conflicts)
|
resolved_conflicts = all_conflicted_by_conflicts.exclude(id__in=still_conflicting_conflicts)
|
||||||
resolved_conflicts.delete()
|
resolved_conflicts.delete()
|
||||||
|
|
||||||
|
|
||||||
def get_data_objects(self):
|
def get_data_objects(self):
|
||||||
""" Getter for all objects which are related to this geometry
|
""" Getter for all objects which are related to this geometry
|
||||||
|
|
||||||
@ -90,6 +93,54 @@ class Geometry(BaseResource):
|
|||||||
objs += set_objs
|
objs += set_objs
|
||||||
return objs
|
return objs
|
||||||
|
|
||||||
|
def update_parcels(self):
|
||||||
|
""" Updates underlying parcel information
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
from konova.models import Parcel, District
|
||||||
|
parcel_fetcher = ParcelWFSFetcher(
|
||||||
|
geometry_id=self.id,
|
||||||
|
)
|
||||||
|
typename = "ave:Flurstueck"
|
||||||
|
fetched_parcels = parcel_fetcher.get_features(
|
||||||
|
typename
|
||||||
|
)
|
||||||
|
underlying_parcels = []
|
||||||
|
for result in fetched_parcels:
|
||||||
|
fetched_parcel = result[typename]
|
||||||
|
parcel_obj = Parcel.objects.get_or_create(
|
||||||
|
gmrkng=fetched_parcel["ave:gemarkung"],
|
||||||
|
flr=fetched_parcel["ave:flur"],
|
||||||
|
flrstck_nnr=fetched_parcel['ave:flstnrnen'],
|
||||||
|
flrstck_zhlr=fetched_parcel['ave:flstnrzae'],
|
||||||
|
)[0]
|
||||||
|
district = District.objects.get_or_create(
|
||||||
|
gmnd=fetched_parcel["ave:gemeinde"],
|
||||||
|
krs=fetched_parcel["ave:kreis"],
|
||||||
|
)[0]
|
||||||
|
parcel_obj.district = district
|
||||||
|
parcel_obj.updated_on = timezone.now()
|
||||||
|
parcel_obj.save()
|
||||||
|
underlying_parcels.append(parcel_obj)
|
||||||
|
|
||||||
|
self.parcels.set(underlying_parcels)
|
||||||
|
|
||||||
|
def get_underlying_parcels(self):
|
||||||
|
""" Getter for related parcels and their districts
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
parcels (QuerySet): The related parcels as queryset
|
||||||
|
"""
|
||||||
|
parcels = self.parcels.all().prefetch_related(
|
||||||
|
"district"
|
||||||
|
).order_by(
|
||||||
|
"gmrkng",
|
||||||
|
)
|
||||||
|
|
||||||
|
return parcels
|
||||||
|
|
||||||
|
|
||||||
class GeometryConflict(UuidModel):
|
class GeometryConflict(UuidModel):
|
||||||
"""
|
"""
|
||||||
|
@ -420,13 +420,29 @@ class GeoReferencedMixin(models.Model):
|
|||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
def _set_geometry_conflict_message(self, request: HttpRequest):
|
def get_underlying_parcels(self):
|
||||||
|
""" Getter for related parcels
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
parcels (Iterable): An empty list or a Queryset
|
||||||
|
"""
|
||||||
|
if self.geometry is not None:
|
||||||
|
return self.geometry.get_underlying_parcels()
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def set_geometry_conflict_message(self, request: HttpRequest):
|
||||||
|
if self.geometry is None:
|
||||||
|
return request
|
||||||
|
|
||||||
instance_objs = []
|
instance_objs = []
|
||||||
add_message = False
|
add_message = False
|
||||||
conflicts = self.geometry.conflicts_geometries.all()
|
conflicts = self.geometry.conflicts_geometries.all()
|
||||||
|
|
||||||
for conflict in conflicts:
|
for conflict in conflicts:
|
||||||
instance_objs += conflict.affected_geometry.get_data_objects()
|
instance_objs += conflict.affected_geometry.get_data_objects()
|
||||||
add_message = True
|
add_message = True
|
||||||
|
|
||||||
conflicts = self.geometry.conflicted_by_geometries.all()
|
conflicts = self.geometry.conflicted_by_geometries.all()
|
||||||
for conflict in conflicts:
|
for conflict in conflicts:
|
||||||
instance_objs += conflict.conflicting_geometry.get_data_objects()
|
instance_objs += conflict.conflicting_geometry.get_data_objects()
|
||||||
|
79
konova/models/parcel.py
Normal file
79
konova/models/parcel.py
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||||
|
Created on: 16.12.21
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
from konova.models import UuidModel
|
||||||
|
|
||||||
|
|
||||||
|
class Parcel(UuidModel):
|
||||||
|
""" 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
|
||||||
|
names in german. Any try to translate them to English result in strange or insufficient translations.
|
||||||
|
|
||||||
|
All fields have to be CharFields as well, since there are e.g. Flurstücksnummer holding e.g. '123____' which
|
||||||
|
can not be realized using numerical fields.
|
||||||
|
|
||||||
|
To avoid conflicts due to german Umlaute, the field names are shortened and vocals are dropped.
|
||||||
|
|
||||||
|
"""
|
||||||
|
geometries = models.ManyToManyField("konova.Geometry", related_name="parcels", blank=True)
|
||||||
|
district = models.ForeignKey("konova.District", on_delete=models.SET_NULL, null=True, blank=True, related_name="parcels")
|
||||||
|
gmrkng = models.CharField(
|
||||||
|
max_length=1000,
|
||||||
|
help_text="Gemarkung",
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
)
|
||||||
|
flrstck_nnr = models.CharField(
|
||||||
|
max_length=1000,
|
||||||
|
help_text="Flurstücksnenner",
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
)
|
||||||
|
flrstck_zhlr = models.CharField(
|
||||||
|
max_length=1000,
|
||||||
|
help_text="Flurstückszähler",
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
)
|
||||||
|
flr = models.CharField(
|
||||||
|
max_length=1000,
|
||||||
|
help_text="Flur",
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
)
|
||||||
|
updated_on = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.gmrkng} | {self.flr} | {self.flrstck_zhlr} | {self.flrstck_nnr}"
|
||||||
|
|
||||||
|
|
||||||
|
class District(UuidModel):
|
||||||
|
""" The model District holds more coarse information, such as Kreis, Verbandsgemeinde and Gemeinde.
|
||||||
|
|
||||||
|
There might be the case that a geometry lies on a hundred Parcel entries but only on one District entry.
|
||||||
|
Therefore a geometry can have a lot of relations to Parcel entries but only a few or only a single one to one
|
||||||
|
District.
|
||||||
|
|
||||||
|
"""
|
||||||
|
gmnd = models.CharField(
|
||||||
|
max_length=1000,
|
||||||
|
help_text="Gemeinde",
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
)
|
||||||
|
krs = models.CharField(
|
||||||
|
max_length=1000,
|
||||||
|
help_text="Kreis",
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.gmnd} | {self.krs}"
|
@ -80,3 +80,8 @@ LANIS_ZOOM_LUT = {
|
|||||||
1000: 30,
|
1000: 30,
|
||||||
500: 31,
|
500: 31,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Parcel WFS settings
|
||||||
|
PARCEL_WFS_BASE_URL = "https://www.geoportal.rlp.de/registry/wfs/519"
|
||||||
|
PARCEL_WFS_USER = "ksp"
|
||||||
|
PARCEL_WFS_PW = "CHANGE_ME"
|
29
konova/templates/konova/includes/parcels.html
Normal file
29
konova/templates/konova/includes/parcels.html
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
<div>
|
||||||
|
<h3>{% trans 'Spatial reference' %}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="table-container w-100 scroll-300">
|
||||||
|
<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>
|
||||||
|
</div>
|
@ -9,7 +9,7 @@ from django.contrib.gis.db.models.functions import Translate
|
|||||||
|
|
||||||
from konova.models import Geometry, GeometryConflict
|
from konova.models import Geometry, GeometryConflict
|
||||||
from konova.tests.test_views import BaseTestCase
|
from konova.tests.test_views import BaseTestCase
|
||||||
from user.models import UserActionLogEntry
|
from konova.utils.wfs.spatial import ParcelWFSFetcher
|
||||||
|
|
||||||
|
|
||||||
class GeometryTestCase(BaseTestCase):
|
class GeometryTestCase(BaseTestCase):
|
||||||
@ -17,16 +17,29 @@ class GeometryTestCase(BaseTestCase):
|
|||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
super().setUpTestData()
|
super().setUpTestData()
|
||||||
geom = cls.create_dummy_geometry()
|
geom = cls.create_dummy_geometry()
|
||||||
action = UserActionLogEntry.get_created_action(cls.superuser)
|
|
||||||
cls.geom_1 = Geometry.objects.create(
|
cls.geom_1 = Geometry.objects.create(
|
||||||
geom=geom,
|
geom=geom,
|
||||||
created=action,
|
|
||||||
)
|
)
|
||||||
cls.geom_2 = Geometry.objects.create(
|
cls.geom_2 = Geometry.objects.create(
|
||||||
geom=geom,
|
geom=geom,
|
||||||
created=action,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_geometry_parcel_caluclation(self):
|
||||||
|
""" Tests whether newly created geometries already have parcels calculated during save
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
has_parcels = self.geom_1.parcels.all().exists()
|
||||||
|
self.assertFalse(has_parcels, msg=f"{self.geom_1.id} has parcels but should not!")
|
||||||
|
self.geom_1.update_parcels()
|
||||||
|
self.geom_1.refresh_from_db()
|
||||||
|
parcels = self.geom_1.parcels.all()
|
||||||
|
has_parcels = parcels.exists()
|
||||||
|
parcel_districts = parcels.values_list("district", flat=True)
|
||||||
|
self.assertTrue(has_parcels, msg=f"{self.geom_1.id} has no parcels but should!")
|
||||||
|
self.assertEqual(parcels.count(), len(parcel_districts), msg=f"Not every parcel has exactly one district!")
|
||||||
|
|
||||||
def test_geometry_conflict(self):
|
def test_geometry_conflict(self):
|
||||||
""" Tests whether a geometry conflict will be present in case of identical/overlaying geometries and
|
""" Tests whether a geometry conflict will be present in case of identical/overlaying geometries and
|
||||||
if the conflict will be resolved if one geometry is edited.
|
if the conflict will be resolved if one geometry is edited.
|
||||||
@ -34,8 +47,9 @@ class GeometryTestCase(BaseTestCase):
|
|||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self.geom_1.check_for_conflicts()
|
conflict = GeometryConflict.objects.all()
|
||||||
conflict = GeometryConflict.objects.all().first()
|
self.assertEqual(1, conflict.count())
|
||||||
|
conflict = conflict.first()
|
||||||
self.assertEqual(conflict.conflicting_geometry, self.geom_2)
|
self.assertEqual(conflict.conflicting_geometry, self.geom_2)
|
||||||
self.assertEqual(conflict.affected_geometry, self.geom_1)
|
self.assertEqual(conflict.affected_geometry, self.geom_1)
|
||||||
|
|
||||||
@ -47,3 +61,19 @@ class GeometryTestCase(BaseTestCase):
|
|||||||
self.geom_1.check_for_conflicts()
|
self.geom_1.check_for_conflicts()
|
||||||
num_conflict = GeometryConflict.objects.all().count()
|
num_conflict = GeometryConflict.objects.all().count()
|
||||||
self.assertEqual(0, num_conflict)
|
self.assertEqual(0, num_conflict)
|
||||||
|
|
||||||
|
def test_wfs_fetch(self):
|
||||||
|
""" Tests the fetching functionality of ParcelWFSFetcher
|
||||||
|
|
||||||
|
+++ Test relies on the availability of the RLP Flurstück WFS +++
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
fetcher = ParcelWFSFetcher(
|
||||||
|
geometry_id=self.geom_1.id,
|
||||||
|
)
|
||||||
|
features = fetcher.get_features(
|
||||||
|
"ave:Flurstueck",
|
||||||
|
)
|
||||||
|
self.assertNotEqual(0, len(features), msg="Spatial wfs get feature did not work!")
|
||||||
|
@ -214,7 +214,7 @@ class BaseTestCase(TestCase):
|
|||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
polygon = Polygon.from_bbox((7.157593, 49.882247, 7.816772, 50.266521))
|
polygon = Polygon.from_bbox((7.592449, 50.359385, 7.593382, 50.359874))
|
||||||
polygon.srid = 4326
|
polygon.srid = 4326
|
||||||
polygon = polygon.transform(3857, clone=True)
|
polygon = polygon.transform(3857, clone=True)
|
||||||
return MultiPolygon(polygon, srid=3857) # 3857 is the default srid used for MultiPolygonField in the form
|
return MultiPolygon(polygon, srid=3857) # 3857 is the default srid used for MultiPolygonField in the form
|
||||||
|
175
konova/utils/wfs/spatial.py
Normal file
175
konova/utils/wfs/spatial.py
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||||
|
Created on: 17.12.21
|
||||||
|
|
||||||
|
"""
|
||||||
|
from abc import abstractmethod
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import xmltodict
|
||||||
|
from django.contrib.gis.db.models.functions import AsGML, Transform
|
||||||
|
from requests.auth import HTTPDigestAuth
|
||||||
|
|
||||||
|
from konova.settings import DEFAULT_SRID_RLP, PARCEL_WFS_USER, PARCEL_WFS_PW
|
||||||
|
|
||||||
|
|
||||||
|
class AbstractWFSFetcher:
|
||||||
|
""" Base class for fetching WFS data
|
||||||
|
|
||||||
|
"""
|
||||||
|
# base_url represents not the capabilities url but the parameter-free base url
|
||||||
|
base_url = None
|
||||||
|
version = None
|
||||||
|
auth_user = None
|
||||||
|
auth_pw = None
|
||||||
|
auth_digest_obj = None
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
|
def __init__(self, base_url: str, version: str = "1.1.0", auth_user: str = None, auth_pw: str = None, *args, **kwargs):
|
||||||
|
self.base_url = base_url
|
||||||
|
self.version = version
|
||||||
|
self.auth_pw = auth_pw
|
||||||
|
self.auth_user = auth_user
|
||||||
|
|
||||||
|
self._create_auth_obj()
|
||||||
|
|
||||||
|
def _create_auth_obj(self):
|
||||||
|
if self.auth_pw is not None and self.auth_user is not None:
|
||||||
|
self.auth_digest_obj = HTTPDigestAuth(
|
||||||
|
self.auth_user,
|
||||||
|
self.auth_pw
|
||||||
|
)
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_features(self, feature_identifier: str, filter_str: str):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
class ParcelWFSFetcher(AbstractWFSFetcher):
|
||||||
|
""" Fetches features from a special parcel WFS
|
||||||
|
|
||||||
|
"""
|
||||||
|
geometry_id = None
|
||||||
|
geometry_property_name = None
|
||||||
|
count = 100
|
||||||
|
|
||||||
|
def __init__(self, geometry_id: str, geometry_property_name: str = "msGeometry", *args, **kwargs):
|
||||||
|
super().__init__(
|
||||||
|
version="2.0.0",
|
||||||
|
base_url="https://www.geoportal.rlp.de/registry/wfs/519",
|
||||||
|
auth_user=PARCEL_WFS_USER,
|
||||||
|
auth_pw=PARCEL_WFS_PW,
|
||||||
|
*args,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
self.geometry_id = geometry_id
|
||||||
|
self.geometry_property_name = geometry_property_name
|
||||||
|
|
||||||
|
def _create_spatial_filter(self,
|
||||||
|
geometry_operation: str,
|
||||||
|
filter_srid: str = None):
|
||||||
|
""" Creates a xml spatial filter according to the WFS filter specification
|
||||||
|
|
||||||
|
Args:
|
||||||
|
geometry_operation (str): One of the WFS supported spatial filter operations (according to capabilities)
|
||||||
|
filter_srid (str): Used to transform the geometry into the spatial reference system identified by this srid
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
spatial_filter (str): The spatial filter element
|
||||||
|
"""
|
||||||
|
from konova.models import Geometry
|
||||||
|
if filter_srid is None:
|
||||||
|
filter_srid = DEFAULT_SRID_RLP
|
||||||
|
geom_gml = Geometry.objects.filter(
|
||||||
|
id=self.geometry_id
|
||||||
|
).annotate(
|
||||||
|
transformed=Transform(srid=filter_srid, expression="geom")
|
||||||
|
).annotate(
|
||||||
|
gml=AsGML('transformed')
|
||||||
|
).first().gml
|
||||||
|
spatial_filter = f"<Filter><{geometry_operation}><PropertyName>{self.geometry_property_name}</PropertyName>{geom_gml}</{geometry_operation}></Filter>"
|
||||||
|
return spatial_filter
|
||||||
|
|
||||||
|
def _create_post_data(self,
|
||||||
|
geometry_operation: str,
|
||||||
|
filter_srid: str = None,
|
||||||
|
typenames: str = None,
|
||||||
|
start_index: int = 0,
|
||||||
|
):
|
||||||
|
""" Creates a POST body content for fetching features
|
||||||
|
|
||||||
|
Args:
|
||||||
|
geometry_operation (str): One of the WFS supported spatial filter operations (according to capabilities)
|
||||||
|
filter_srid (str): Used to transform the geometry into the spatial reference system identified by this srid
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
_filter (str): A proper xml WFS filter
|
||||||
|
"""
|
||||||
|
start_index = str(start_index)
|
||||||
|
spatial_filter = self._create_spatial_filter(
|
||||||
|
geometry_operation,
|
||||||
|
filter_srid
|
||||||
|
)
|
||||||
|
_filter = f'<wfs:GetFeature service="WFS" version="{self.version}" xmlns:wfs="http://www.opengis.net/wfs/2.0" xmlns:fes="http://www.opengis.net/fes/2.0" xmlns:myns="http://www.someserver.com/myns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wfs/2.0 http://schemas.opengis.net/wfs/2.0.0/wfs.xsd" count="{self.count}" startindex="{start_index}"><wfs:Query typeNames="{typenames}">{spatial_filter}</wfs:Query></wfs:GetFeature>'
|
||||||
|
return _filter
|
||||||
|
|
||||||
|
def get_features(self,
|
||||||
|
typenames: str,
|
||||||
|
spatial_operator: str = "Intersects",
|
||||||
|
filter_srid: str = None,
|
||||||
|
start_index: int = 0,
|
||||||
|
):
|
||||||
|
""" Fetches features from the WFS using POST
|
||||||
|
|
||||||
|
POST is required since GET has a character limit around 4000. Having a larger filter would result in errors,
|
||||||
|
which do not occur in case of POST.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
typenames (str): References to parameter 'typenames' in a WFS GetFeature request
|
||||||
|
spatial_operator (str): Defines the spatial operation for filtering
|
||||||
|
filter_srid (str): Defines the spatial reference system, the geometry shall be transformed into for filtering
|
||||||
|
start_index (str): References to parameter 'startindex' in a
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
features (list): A list of returned features
|
||||||
|
"""
|
||||||
|
features = []
|
||||||
|
while start_index is not None:
|
||||||
|
post_body = self._create_post_data(
|
||||||
|
spatial_operator,
|
||||||
|
filter_srid,
|
||||||
|
typenames,
|
||||||
|
start_index
|
||||||
|
)
|
||||||
|
response = requests.post(
|
||||||
|
url=self.base_url,
|
||||||
|
data=post_body,
|
||||||
|
auth=self.auth_digest_obj
|
||||||
|
)
|
||||||
|
|
||||||
|
content = response.content.decode("utf-8")
|
||||||
|
content = xmltodict.parse(content)
|
||||||
|
collection = content.get(
|
||||||
|
"wfs:FeatureCollection",
|
||||||
|
{},
|
||||||
|
)
|
||||||
|
|
||||||
|
members = collection.get(
|
||||||
|
"wfs:member",
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
if len(members) > 1:
|
||||||
|
features += members
|
||||||
|
else:
|
||||||
|
features += [members]
|
||||||
|
|
||||||
|
if collection.get("@next", None) is not None:
|
||||||
|
start_index += self.count
|
||||||
|
else:
|
||||||
|
start_index = None
|
||||||
|
|
||||||
|
return features
|
Binary file not shown.
@ -11,15 +11,15 @@
|
|||||||
#: intervention/forms/forms.py:52 intervention/forms/forms.py:154
|
#: intervention/forms/forms.py:52 intervention/forms/forms.py:154
|
||||||
#: intervention/forms/forms.py:166 intervention/forms/modalForms.py:125
|
#: intervention/forms/forms.py:166 intervention/forms/modalForms.py:125
|
||||||
#: intervention/forms/modalForms.py:138 intervention/forms/modalForms.py:151
|
#: intervention/forms/modalForms.py:138 intervention/forms/modalForms.py:151
|
||||||
#: konova/forms.py:139 konova/forms.py:240 konova/forms.py:308
|
#: konova/forms.py:139 konova/forms.py:240 konova/forms.py:309
|
||||||
#: konova/forms.py:335 konova/forms.py:345 konova/forms.py:358
|
#: konova/forms.py:336 konova/forms.py:346 konova/forms.py:359
|
||||||
#: konova/forms.py:370 konova/forms.py:388 user/forms.py:38
|
#: konova/forms.py:371 konova/forms.py:389 user/forms.py:38
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2021-12-16 09:17+0100\n"
|
"POT-Creation-Date: 2022-01-05 14:04+0100\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@ -329,7 +329,7 @@ msgstr "Automatisch generiert"
|
|||||||
#: intervention/templates/intervention/detail/includes/documents.html:28
|
#: intervention/templates/intervention/detail/includes/documents.html:28
|
||||||
#: intervention/templates/intervention/detail/view.html:31
|
#: intervention/templates/intervention/detail/view.html:31
|
||||||
#: intervention/templates/intervention/report/report.html:12
|
#: intervention/templates/intervention/report/report.html:12
|
||||||
#: konova/forms.py:334
|
#: konova/forms.py:335
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "Bezeichnung"
|
msgstr "Bezeichnung"
|
||||||
|
|
||||||
@ -356,7 +356,7 @@ msgstr "Kompensation XY; Flur ABC"
|
|||||||
#: intervention/templates/intervention/detail/includes/documents.html:31
|
#: intervention/templates/intervention/detail/includes/documents.html:31
|
||||||
#: intervention/templates/intervention/detail/includes/payments.html:34
|
#: intervention/templates/intervention/detail/includes/payments.html:34
|
||||||
#: intervention/templates/intervention/detail/includes/revocation.html:38
|
#: intervention/templates/intervention/detail/includes/revocation.html:38
|
||||||
#: konova/forms.py:369 konova/templates/konova/comment_card.html:16
|
#: konova/forms.py:370 konova/templates/konova/includes/comment_card.html:16
|
||||||
msgid "Comment"
|
msgid "Comment"
|
||||||
msgstr "Kommentar"
|
msgstr "Kommentar"
|
||||||
|
|
||||||
@ -472,7 +472,7 @@ msgstr "Zahlung wird an diesem Datum erwartet"
|
|||||||
|
|
||||||
#: compensation/forms/modalForms.py:62 compensation/forms/modalForms.py:239
|
#: compensation/forms/modalForms.py:62 compensation/forms/modalForms.py:239
|
||||||
#: compensation/forms/modalForms.py:317 intervention/forms/modalForms.py:152
|
#: compensation/forms/modalForms.py:317 intervention/forms/modalForms.py:152
|
||||||
#: konova/forms.py:371
|
#: konova/forms.py:372
|
||||||
msgid "Additional comment, maximum {} letters"
|
msgid "Additional comment, maximum {} letters"
|
||||||
msgstr "Zusätzlicher Kommentar, maximal {} Zeichen"
|
msgstr "Zusätzlicher Kommentar, maximal {} Zeichen"
|
||||||
|
|
||||||
@ -793,7 +793,7 @@ msgstr "Dokumente"
|
|||||||
#: compensation/templates/compensation/detail/eco_account/includes/documents.html:14
|
#: compensation/templates/compensation/detail/eco_account/includes/documents.html:14
|
||||||
#: ema/templates/ema/detail/includes/documents.html:14
|
#: ema/templates/ema/detail/includes/documents.html:14
|
||||||
#: intervention/templates/intervention/detail/includes/documents.html:14
|
#: intervention/templates/intervention/detail/includes/documents.html:14
|
||||||
#: konova/forms.py:387
|
#: konova/forms.py:388
|
||||||
msgid "Add new document"
|
msgid "Add new document"
|
||||||
msgstr "Neues Dokument hinzufügen"
|
msgstr "Neues Dokument hinzufügen"
|
||||||
|
|
||||||
@ -1056,41 +1056,41 @@ msgstr "Kompensation {} hinzugefügt"
|
|||||||
msgid "Compensation {} edited"
|
msgid "Compensation {} edited"
|
||||||
msgstr "Kompensation {} bearbeitet"
|
msgstr "Kompensation {} bearbeitet"
|
||||||
|
|
||||||
#: compensation/views/compensation.py:228 compensation/views/eco_account.py:307
|
#: compensation/views/compensation.py:230 compensation/views/eco_account.py:309
|
||||||
#: ema/views.py:181 intervention/views.py:474
|
#: ema/views.py:181 intervention/views.py:477
|
||||||
msgid "Log"
|
msgid "Log"
|
||||||
msgstr "Log"
|
msgstr "Log"
|
||||||
|
|
||||||
#: compensation/views/compensation.py:251
|
#: compensation/views/compensation.py:253
|
||||||
msgid "Compensation removed"
|
msgid "Compensation removed"
|
||||||
msgstr "Kompensation entfernt"
|
msgstr "Kompensation entfernt"
|
||||||
|
|
||||||
#: compensation/views/compensation.py:272 compensation/views/eco_account.py:459
|
#: compensation/views/compensation.py:274 compensation/views/eco_account.py:461
|
||||||
#: ema/views.py:348 intervention/views.py:129
|
#: ema/views.py:348 intervention/views.py:129
|
||||||
msgid "Document added"
|
msgid "Document added"
|
||||||
msgstr "Dokument hinzugefügt"
|
msgstr "Dokument hinzugefügt"
|
||||||
|
|
||||||
#: compensation/views/compensation.py:341 compensation/views/eco_account.py:353
|
#: compensation/views/compensation.py:343 compensation/views/eco_account.py:355
|
||||||
#: ema/views.py:286
|
#: ema/views.py:286
|
||||||
msgid "State added"
|
msgid "State added"
|
||||||
msgstr "Zustand hinzugefügt"
|
msgstr "Zustand hinzugefügt"
|
||||||
|
|
||||||
#: compensation/views/compensation.py:362 compensation/views/eco_account.py:374
|
#: compensation/views/compensation.py:364 compensation/views/eco_account.py:376
|
||||||
#: ema/views.py:307
|
#: ema/views.py:307
|
||||||
msgid "Action added"
|
msgid "Action added"
|
||||||
msgstr "Maßnahme hinzugefügt"
|
msgstr "Maßnahme hinzugefügt"
|
||||||
|
|
||||||
#: compensation/views/compensation.py:383 compensation/views/eco_account.py:439
|
#: compensation/views/compensation.py:385 compensation/views/eco_account.py:441
|
||||||
#: ema/views.py:328
|
#: ema/views.py:328
|
||||||
msgid "Deadline added"
|
msgid "Deadline added"
|
||||||
msgstr "Frist/Termin hinzugefügt"
|
msgstr "Frist/Termin hinzugefügt"
|
||||||
|
|
||||||
#: compensation/views/compensation.py:405 compensation/views/eco_account.py:396
|
#: compensation/views/compensation.py:407 compensation/views/eco_account.py:398
|
||||||
#: ema/views.py:418
|
#: ema/views.py:418
|
||||||
msgid "State removed"
|
msgid "State removed"
|
||||||
msgstr "Zustand gelöscht"
|
msgstr "Zustand gelöscht"
|
||||||
|
|
||||||
#: compensation/views/compensation.py:427 compensation/views/eco_account.py:418
|
#: compensation/views/compensation.py:429 compensation/views/eco_account.py:420
|
||||||
#: ema/views.py:440
|
#: ema/views.py:440
|
||||||
msgid "Action removed"
|
msgid "Action removed"
|
||||||
msgstr "Maßnahme entfernt"
|
msgstr "Maßnahme entfernt"
|
||||||
@ -1103,45 +1103,45 @@ msgstr "Ökokonto {} hinzugefügt"
|
|||||||
msgid "Eco-Account {} edited"
|
msgid "Eco-Account {} edited"
|
||||||
msgstr "Ökokonto {} bearbeitet"
|
msgstr "Ökokonto {} bearbeitet"
|
||||||
|
|
||||||
#: compensation/views/eco_account.py:255
|
#: compensation/views/eco_account.py:257
|
||||||
msgid "Eco-account removed"
|
msgid "Eco-account removed"
|
||||||
msgstr "Ökokonto entfernt"
|
msgstr "Ökokonto entfernt"
|
||||||
|
|
||||||
#: compensation/views/eco_account.py:283
|
#: compensation/views/eco_account.py:285
|
||||||
msgid "Deduction removed"
|
msgid "Deduction removed"
|
||||||
msgstr "Abbuchung entfernt"
|
msgstr "Abbuchung entfernt"
|
||||||
|
|
||||||
#: compensation/views/eco_account.py:328 ema/views.py:261
|
#: compensation/views/eco_account.py:330 ema/views.py:261
|
||||||
#: intervention/views.py:516
|
#: intervention/views.py:519
|
||||||
msgid "{} unrecorded"
|
msgid "{} unrecorded"
|
||||||
msgstr "{} entzeichnet"
|
msgstr "{} entzeichnet"
|
||||||
|
|
||||||
#: compensation/views/eco_account.py:328 ema/views.py:261
|
#: compensation/views/eco_account.py:330 ema/views.py:261
|
||||||
#: intervention/views.py:516
|
#: intervention/views.py:519
|
||||||
msgid "{} recorded"
|
msgid "{} recorded"
|
||||||
msgstr "{} verzeichnet"
|
msgstr "{} verzeichnet"
|
||||||
|
|
||||||
#: compensation/views/eco_account.py:529 intervention/views.py:497
|
#: compensation/views/eco_account.py:531 intervention/views.py:500
|
||||||
msgid "Deduction added"
|
msgid "Deduction added"
|
||||||
msgstr "Abbuchung hinzugefügt"
|
msgstr "Abbuchung hinzugefügt"
|
||||||
|
|
||||||
#: compensation/views/eco_account.py:612 ema/views.py:516
|
#: compensation/views/eco_account.py:614 ema/views.py:516
|
||||||
#: intervention/views.py:372
|
#: intervention/views.py:375
|
||||||
msgid "{} has already been shared with you"
|
msgid "{} has already been shared with you"
|
||||||
msgstr "{} wurde bereits für Sie freigegeben"
|
msgstr "{} wurde bereits für Sie freigegeben"
|
||||||
|
|
||||||
#: compensation/views/eco_account.py:617 ema/views.py:521
|
#: compensation/views/eco_account.py:619 ema/views.py:521
|
||||||
#: intervention/views.py:377
|
#: intervention/views.py:380
|
||||||
msgid "{} has been shared with you"
|
msgid "{} has been shared with you"
|
||||||
msgstr "{} ist nun für Sie freigegeben"
|
msgstr "{} ist nun für Sie freigegeben"
|
||||||
|
|
||||||
#: compensation/views/eco_account.py:624 ema/views.py:528
|
#: compensation/views/eco_account.py:626 ema/views.py:528
|
||||||
#: intervention/views.py:384
|
#: intervention/views.py:387
|
||||||
msgid "Share link invalid"
|
msgid "Share link invalid"
|
||||||
msgstr "Freigabelink ungültig"
|
msgstr "Freigabelink ungültig"
|
||||||
|
|
||||||
#: compensation/views/eco_account.py:647 ema/views.py:551
|
#: compensation/views/eco_account.py:649 ema/views.py:551
|
||||||
#: intervention/views.py:407
|
#: intervention/views.py:410
|
||||||
msgid "Share settings updated"
|
msgid "Share settings updated"
|
||||||
msgstr "Freigabe Einstellungen aktualisiert"
|
msgstr "Freigabe Einstellungen aktualisiert"
|
||||||
|
|
||||||
@ -1333,7 +1333,7 @@ msgstr "Kompensationen und Zahlungen geprüft"
|
|||||||
msgid "Run check"
|
msgid "Run check"
|
||||||
msgstr "Prüfung vornehmen"
|
msgstr "Prüfung vornehmen"
|
||||||
|
|
||||||
#: intervention/forms/modalForms.py:196 konova/forms.py:453
|
#: intervention/forms/modalForms.py:196 konova/forms.py:454
|
||||||
msgid ""
|
msgid ""
|
||||||
"I, {} {}, confirm that all necessary control steps have been performed by "
|
"I, {} {}, confirm that all necessary control steps have been performed by "
|
||||||
"myself."
|
"myself."
|
||||||
@ -1472,31 +1472,31 @@ msgstr ""
|
|||||||
msgid "Intervention {} added"
|
msgid "Intervention {} added"
|
||||||
msgstr "Eingriff {} hinzugefügt"
|
msgstr "Eingriff {} hinzugefügt"
|
||||||
|
|
||||||
#: intervention/views.py:243
|
#: intervention/views.py:245
|
||||||
msgid "This intervention has {} revocations"
|
msgid "This intervention has {} revocations"
|
||||||
msgstr "Dem Eingriff liegen {} Widersprüche vor"
|
msgstr "Dem Eingriff liegen {} Widersprüche vor"
|
||||||
|
|
||||||
#: intervention/views.py:290
|
#: intervention/views.py:293
|
||||||
msgid "Intervention {} edited"
|
msgid "Intervention {} edited"
|
||||||
msgstr "Eingriff {} bearbeitet"
|
msgstr "Eingriff {} bearbeitet"
|
||||||
|
|
||||||
#: intervention/views.py:325
|
#: intervention/views.py:328
|
||||||
msgid "{} removed"
|
msgid "{} removed"
|
||||||
msgstr "{} entfernt"
|
msgstr "{} entfernt"
|
||||||
|
|
||||||
#: intervention/views.py:346
|
#: intervention/views.py:349
|
||||||
msgid "Revocation removed"
|
msgid "Revocation removed"
|
||||||
msgstr "Widerspruch entfernt"
|
msgstr "Widerspruch entfernt"
|
||||||
|
|
||||||
#: intervention/views.py:428
|
#: intervention/views.py:431
|
||||||
msgid "Check performed"
|
msgid "Check performed"
|
||||||
msgstr "Prüfung durchgeführt"
|
msgstr "Prüfung durchgeführt"
|
||||||
|
|
||||||
#: intervention/views.py:450
|
#: intervention/views.py:453
|
||||||
msgid "Revocation added"
|
msgid "Revocation added"
|
||||||
msgstr "Widerspruch hinzugefügt"
|
msgstr "Widerspruch hinzugefügt"
|
||||||
|
|
||||||
#: intervention/views.py:521
|
#: intervention/views.py:524
|
||||||
msgid "There are errors on this intervention:"
|
msgid "There are errors on this intervention:"
|
||||||
msgstr "Es liegen Fehler in diesem Eingriff vor:"
|
msgstr "Es liegen Fehler in diesem Eingriff vor:"
|
||||||
|
|
||||||
@ -1525,11 +1525,11 @@ msgstr "Speichern"
|
|||||||
msgid "Not editable"
|
msgid "Not editable"
|
||||||
msgstr "Nicht editierbar"
|
msgstr "Nicht editierbar"
|
||||||
|
|
||||||
#: konova/forms.py:138 konova/forms.py:307
|
#: konova/forms.py:138 konova/forms.py:308
|
||||||
msgid "Confirm"
|
msgid "Confirm"
|
||||||
msgstr "Bestätige"
|
msgstr "Bestätige"
|
||||||
|
|
||||||
#: konova/forms.py:150 konova/forms.py:316
|
#: konova/forms.py:150 konova/forms.py:317
|
||||||
msgid "Remove"
|
msgid "Remove"
|
||||||
msgstr "Löschen"
|
msgstr "Löschen"
|
||||||
|
|
||||||
@ -1542,56 +1542,56 @@ msgstr "Sie sind dabei {} {} zu löschen"
|
|||||||
msgid "Geometry"
|
msgid "Geometry"
|
||||||
msgstr "Geometrie"
|
msgstr "Geometrie"
|
||||||
|
|
||||||
#: konova/forms.py:317
|
#: konova/forms.py:318
|
||||||
msgid "Are you sure?"
|
msgid "Are you sure?"
|
||||||
msgstr "Sind Sie sicher?"
|
msgstr "Sind Sie sicher?"
|
||||||
|
|
||||||
#: konova/forms.py:344
|
#: konova/forms.py:345
|
||||||
msgid "Created on"
|
msgid "Created on"
|
||||||
msgstr "Erstellt"
|
msgstr "Erstellt"
|
||||||
|
|
||||||
#: konova/forms.py:346
|
#: konova/forms.py:347
|
||||||
msgid "When has this file been created? Important for photos."
|
msgid "When has this file been created? Important for photos."
|
||||||
msgstr "Wann wurde diese Datei erstellt oder das Foto aufgenommen?"
|
msgstr "Wann wurde diese Datei erstellt oder das Foto aufgenommen?"
|
||||||
|
|
||||||
#: konova/forms.py:357
|
#: konova/forms.py:358
|
||||||
#: venv/lib/python3.7/site-packages/django/db/models/fields/files.py:231
|
#: venv/lib/python3.7/site-packages/django/db/models/fields/files.py:231
|
||||||
msgid "File"
|
msgid "File"
|
||||||
msgstr "Datei"
|
msgstr "Datei"
|
||||||
|
|
||||||
#: konova/forms.py:359
|
#: konova/forms.py:360
|
||||||
msgid "Allowed formats: pdf, jpg, png. Max size 15 MB."
|
msgid "Allowed formats: pdf, jpg, png. Max size 15 MB."
|
||||||
msgstr "Formate: pdf, jpg, png. Maximal 15 MB."
|
msgstr "Formate: pdf, jpg, png. Maximal 15 MB."
|
||||||
|
|
||||||
#: konova/forms.py:405
|
#: konova/forms.py:406
|
||||||
msgid "Unsupported file type"
|
msgid "Unsupported file type"
|
||||||
msgstr "Dateiformat nicht unterstützt"
|
msgstr "Dateiformat nicht unterstützt"
|
||||||
|
|
||||||
#: konova/forms.py:412
|
#: konova/forms.py:413
|
||||||
msgid "File too large"
|
msgid "File too large"
|
||||||
msgstr "Datei zu groß"
|
msgstr "Datei zu groß"
|
||||||
|
|
||||||
#: konova/forms.py:421
|
#: konova/forms.py:422
|
||||||
msgid "Added document"
|
msgid "Added document"
|
||||||
msgstr "Dokument hinzugefügt"
|
msgstr "Dokument hinzugefügt"
|
||||||
|
|
||||||
#: konova/forms.py:444
|
#: konova/forms.py:445
|
||||||
msgid "Confirm record"
|
msgid "Confirm record"
|
||||||
msgstr "Verzeichnen bestätigen"
|
msgstr "Verzeichnen bestätigen"
|
||||||
|
|
||||||
#: konova/forms.py:452
|
#: konova/forms.py:453
|
||||||
msgid "Record data"
|
msgid "Record data"
|
||||||
msgstr "Daten verzeichnen"
|
msgstr "Daten verzeichnen"
|
||||||
|
|
||||||
#: konova/forms.py:459
|
#: konova/forms.py:460
|
||||||
msgid "Confirm unrecord"
|
msgid "Confirm unrecord"
|
||||||
msgstr "Entzeichnen bestätigen"
|
msgstr "Entzeichnen bestätigen"
|
||||||
|
|
||||||
#: konova/forms.py:460
|
#: konova/forms.py:461
|
||||||
msgid "Unrecord data"
|
msgid "Unrecord data"
|
||||||
msgstr "Daten entzeichnen"
|
msgstr "Daten entzeichnen"
|
||||||
|
|
||||||
#: konova/forms.py:461
|
#: konova/forms.py:462
|
||||||
msgid "I, {} {}, confirm that this data must be unrecorded."
|
msgid "I, {} {}, confirm that this data must be unrecorded."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Ich, {} {}, bestätige, dass diese Daten wieder entzeichnet werden müssen."
|
"Ich, {} {}, bestätige, dass diese Daten wieder entzeichnet werden müssen."
|
||||||
@ -1663,6 +1663,30 @@ msgstr "Anzeigen"
|
|||||||
msgid "Deduct"
|
msgid "Deduct"
|
||||||
msgstr "Abbuchen"
|
msgstr "Abbuchen"
|
||||||
|
|
||||||
|
#: konova/templates/konova/includes/parcels.html:3
|
||||||
|
msgid "Spatial reference"
|
||||||
|
msgstr "Raumreferenz"
|
||||||
|
|
||||||
|
#: konova/templates/konova/includes/parcels.html:9
|
||||||
|
msgid "Kreis"
|
||||||
|
msgstr "Kreis"
|
||||||
|
|
||||||
|
#: konova/templates/konova/includes/parcels.html:10
|
||||||
|
msgid "Gemarkung"
|
||||||
|
msgstr "Gemarkung"
|
||||||
|
|
||||||
|
#: konova/templates/konova/includes/parcels.html:11
|
||||||
|
msgid "Parcel"
|
||||||
|
msgstr "Flur"
|
||||||
|
|
||||||
|
#: konova/templates/konova/includes/parcels.html:12
|
||||||
|
msgid "Parcel counter"
|
||||||
|
msgstr "Flurstückzähler"
|
||||||
|
|
||||||
|
#: konova/templates/konova/includes/parcels.html:13
|
||||||
|
msgid "Parcel number"
|
||||||
|
msgstr "Flurstücknenner"
|
||||||
|
|
||||||
#: konova/templates/konova/widgets/generate-content-input.html:6
|
#: konova/templates/konova/widgets/generate-content-input.html:6
|
||||||
msgid "Generate new"
|
msgid "Generate new"
|
||||||
msgstr "Neu generieren"
|
msgstr "Neu generieren"
|
||||||
@ -1726,8 +1750,8 @@ msgid ""
|
|||||||
"Action canceled. Eco account is recorded or deductions exist. Only "
|
"Action canceled. Eco account is recorded or deductions exist. Only "
|
||||||
"conservation office member can perform this action."
|
"conservation office member can perform this action."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Aktion abgebrochen. Ökokonto ist bereits verzeichnet oder Abbuchungen liegen vor. Nur "
|
"Aktion abgebrochen. Ökokonto ist bereits verzeichnet oder Abbuchungen liegen "
|
||||||
"Eintragungsstellennutzer können diese Aktion jetzt durchführen."
|
"vor. Nur Eintragungsstellennutzer können diese Aktion jetzt durchführen."
|
||||||
|
|
||||||
#: konova/utils/message_templates.py:25
|
#: konova/utils/message_templates.py:25
|
||||||
msgid "Edited general data"
|
msgid "Edited general data"
|
||||||
|
@ -14,10 +14,14 @@ django-tables2==2.3.4
|
|||||||
et-xmlfile==1.1.0
|
et-xmlfile==1.1.0
|
||||||
idna==2.10
|
idna==2.10
|
||||||
importlib-metadata==2.1.1
|
importlib-metadata==2.1.1
|
||||||
itsdangerous<1.0.0
|
itsdangerous==0.24
|
||||||
openpyxl==3.0.9
|
openpyxl==3.0.9
|
||||||
|
OWSLib==0.25.0
|
||||||
psycopg2-binary==2.9.1
|
psycopg2-binary==2.9.1
|
||||||
|
pyproj==3.2.1
|
||||||
|
python-dateutil==2.8.2
|
||||||
pytz==2020.4
|
pytz==2020.4
|
||||||
|
PyYAML==6.0
|
||||||
qrcode==7.3.1
|
qrcode==7.3.1
|
||||||
requests==2.25.0
|
requests==2.25.0
|
||||||
six==1.15.0
|
six==1.15.0
|
||||||
@ -25,4 +29,5 @@ soupsieve==2.2.1
|
|||||||
sqlparse==0.4.1
|
sqlparse==0.4.1
|
||||||
urllib3==1.26.2
|
urllib3==1.26.2
|
||||||
webservices==0.7
|
webservices==0.7
|
||||||
|
xmltodict==0.12.0
|
||||||
zipp==3.4.1
|
zipp==3.4.1
|
||||||
|
Loading…
Reference in New Issue
Block a user