From bdbe2e91ce333909cc2920e5a622fb762cdff67f Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Wed, 1 Feb 2023 14:08:39 +0100 Subject: [PATCH 1/6] Quality Check Command * adds new command 'quality_check' which performs the quality checker on certain entries, which can be filtered using '--identifier-like' and/or '--title-like' parameters * results are shown in terminal --- konova/management/commands/quality_check.py | 69 +++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 konova/management/commands/quality_check.py diff --git a/konova/management/commands/quality_check.py b/konova/management/commands/quality_check.py new file mode 100644 index 00000000..1100e04a --- /dev/null +++ b/konova/management/commands/quality_check.py @@ -0,0 +1,69 @@ +""" +Author: Michel Peltriaux +Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany +Contact: ksp-servicestelle@sgdnord.rlp.de +Created on: 01.02.23 + +""" +from compensation.models import Compensation, EcoAccount +from compensation.utils.quality import CompensationQualityChecker, EcoAccountQualityChecker +from ema.models import Ema +from ema.utils.quality import EmaQualityChecker +from intervention.models import Intervention +from intervention.utils.quality import InterventionQualityChecker +from konova.management.commands.setup import BaseKonovaCommand + + +class Command(BaseKonovaCommand): + help = "Runs quality check on certain entries" + + __interventions = [] + __compensations = [] + __ecoaccount = [] + __ema = [] + identifier_like = None + title_like = None + + def add_arguments(self, parser): + try: + parser.add_argument("--identifier-like", type=str) + parser.add_argument("--title-like", type=str) + except ValueError as e: + self._write_error(f"Argument error: {e}") + exit(-1) + + def handle(self, *args, **options): + self.__handle_arguments(options) + self.__get_objects() + self.perform_quality_check() + + def __handle_arguments(self, options): + self.identifier_like = options["identifier_like"] or "" + self.title_like = options["title_like"] or "" + + def __get_objects(self): + _filter = { + "identifier__icontains": self.identifier_like, + "title__icontains": self.title_like, + } + self.__interventions = Intervention.objects.filter(**_filter) + self.__compensations = Compensation.objects.filter(**_filter) + self.__ecoaccount = EcoAccount.objects.filter(**_filter) + self.__ema = Ema.objects.filter(**_filter) + + def perform_quality_check(self): + # Interventions + _runs = [ + (self.__interventions, InterventionQualityChecker), + (self.__compensations, CompensationQualityChecker), + (self.__ecoaccount, EcoAccountQualityChecker), + (self.__ema, EmaQualityChecker), + ] + for run in _runs: + entries = run[0] + CheckerClass = run[1] + for entry in entries: + checker = CheckerClass(entry) + checker.run_check() + if not checker.valid: + self._write_error(f"{entry.identifier};{';'.join(checker.messages)}") From 93cc17b01a17e01e3b37587b07dfb056c43a7846 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Wed, 1 Feb 2023 14:17:05 +0100 Subject: [PATCH 2/6] Quality Check Command enhancement * adds fix for dealing with __proxy__ instances --- konova/management/commands/quality_check.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/konova/management/commands/quality_check.py b/konova/management/commands/quality_check.py index 1100e04a..a58bda11 100644 --- a/konova/management/commands/quality_check.py +++ b/konova/management/commands/quality_check.py @@ -66,4 +66,4 @@ class Command(BaseKonovaCommand): checker = CheckerClass(entry) checker.run_check() if not checker.valid: - self._write_error(f"{entry.identifier};{';'.join(checker.messages)}") + self._write_error(f"{entry.identifier};{';'.join(str(msg) for msg in checker.messages)}") From 676a76acf329069f732e7bae9103ec57f72cfe04 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Thu, 2 Feb 2023 16:34:09 +0100 Subject: [PATCH 3/6] Bugfix * fixes rendering of shared users counter on unshared compensation entries --- .../templates/compensation/detail/compensation/view.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compensation/templates/compensation/detail/compensation/view.html b/compensation/templates/compensation/detail/compensation/view.html index b40fc2f4..26800a83 100644 --- a/compensation/templates/compensation/detail/compensation/view.html +++ b/compensation/templates/compensation/detail/compensation/view.html @@ -130,7 +130,7 @@ {% else %} {% fa5_icon 'eye-slash' %} - {{obj.users.count}} {% trans 'other users' %} + {{obj.intervention.users.count}} {% trans 'other users' %} {% endif %} From c0ff113ff2bd4d5493d6bdc79a4768b251020dd8 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Mon, 6 Feb 2023 15:00:34 +0100 Subject: [PATCH 4/6] #290 Egon exporter file name * replace user given file name with file based file name for egon export handling --- intervention/utils/egon_export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intervention/utils/egon_export.py b/intervention/utils/egon_export.py index 2589595f..653a630a 100644 --- a/intervention/utils/egon_export.py +++ b/intervention/utils/egon_export.py @@ -152,7 +152,7 @@ class EgonGmlBuilder: "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:dateiname": doc.file.url.split("/")[-1], "oneo:hauptfoto": False, } } for doc in regular_docs From dac060e62d5288a214b0c7602979b2d6a5e3837a Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Mon, 13 Feb 2023 09:58:56 +0100 Subject: [PATCH 5/6] # Server proxy for client parcel wfs * refactors map_proxy.py * adds proxy support for parcel wfs --- konova/sub_settings/proxy_settings.py | 5 +- konova/urls.py | 5 +- konova/views/map_proxy.py | 93 ++++++++++++++++++++------- templates/map/client/config.json | 1 + 4 files changed, 79 insertions(+), 25 deletions(-) diff --git a/konova/sub_settings/proxy_settings.py b/konova/sub_settings/proxy_settings.py index 4867af10..559344a7 100644 --- a/konova/sub_settings/proxy_settings.py +++ b/konova/sub_settings/proxy_settings.py @@ -10,4 +10,7 @@ proxy = "" PROXIES = { "http": proxy, "https": proxy, -} \ No newline at end of file +} + +CLIENT_PROXY_AUTH_USER = "CHANGE_ME" +CLIENT_PROXY_AUTH_PASSWORD = "CHANGE_ME" \ No newline at end of file diff --git a/konova/urls.py b/konova/urls.py index a8c2426a..a3672d47 100644 --- a/konova/urls.py +++ b/konova/urls.py @@ -22,7 +22,7 @@ from konova.sso.sso import KonovaSSOClient from konova.views.logout import logout_view from konova.views.geometry import get_geom_parcels, get_geom_parcels_content from konova.views.home import home_view -from konova.views.map_proxy import map_client_proxy_view +from konova.views.map_proxy import ClientProxyParcelSearch, ClientProxyParcelWFS sso_client = KonovaSSOClient(SSO_SERVER, SSO_PUBLIC_KEY, SSO_PRIVATE_KEY) urlpatterns = [ @@ -40,7 +40,8 @@ urlpatterns = [ path('api/', include("api.urls")), path('geom//parcels/', get_geom_parcels, name="geometry-parcels"), path('geom//parcels/', get_geom_parcels_content, name="geometry-parcels-content"), - path('client/proxy', map_client_proxy_view, name="map-client-proxy"), + path('client/proxy', ClientProxyParcelSearch.as_view(), name="client-proxy-search"), + path('client/proxy/wfs', ClientProxyParcelWFS.as_view(), name="client-proxy-wfs"), ] if DEBUG: diff --git a/konova/views/map_proxy.py b/konova/views/map_proxy.py index 5f3abc01..9aba1cdd 100644 --- a/konova/views/map_proxy.py +++ b/konova/views/map_proxy.py @@ -10,31 +10,80 @@ import json import requests from django.contrib.auth.decorators import login_required from django.http import JsonResponse, HttpRequest +from django.utils.decorators import method_decorator +from django.utils.http import urlencode +from django.views import View +from requests.auth import HTTPDigestAuth -from konova.sub_settings.proxy_settings import PROXIES +from konova.sub_settings.proxy_settings import PROXIES, CLIENT_PROXY_AUTH_USER, CLIENT_PROXY_AUTH_PASSWORD -@login_required -def map_client_proxy_view(request: HttpRequest): +class BaseClientProxyView(View): """ 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, proxies=PROXIES) - content = response.content - if isinstance(content, bytes): - content = content.decode("utf-8") - body = json.loads(content) - if response.status_code != 200: - return JsonResponse({ - "status_code": response.status_code, - "content": body, - }) - return JsonResponse(body) + class Meta: + abstract = True + + @method_decorator(login_required) + def dispatch(self, request, *args, **kwargs): + return super().dispatch(request, *args, **kwargs) + + def perform_url_call(self, url, headers={}, auth=None): + """ Generic proxied call + + Args: + url (str): The url to call + headers (dict): Optional headers + auth: Optional authentication info + + Returns: + + """ + response = requests.get( + url, + proxies=PROXIES, + headers=headers, + auth=auth + ) + content = response.content + if isinstance(content, bytes): + content = content.decode("utf-8") + return content, response.status_code + + +class ClientProxyParcelSearch(BaseClientProxyView): + + def get(self, request: HttpRequest): + url = request.META.get("QUERY_STRING") + content, response_code = self.perform_url_call(url) + body = json.loads(content) + if response_code != 200: + return JsonResponse({ + "status_code": response_code, + "content": body, + }) + return JsonResponse(body) + + +class ClientProxyParcelWFS(BaseClientProxyView): + + def get(self, request: HttpRequest): + params = request.GET.dict() + params["version"] = "2.0.0" + params["outputformat"] = "application/json; subtype=geojson" + base_url = "https://www.geoportal.rlp.de/registry/wfs/519" + url = f"{base_url}?{urlencode(params, doseq=True)}" + + url = url.replace("typename", "typenames") + auth = HTTPDigestAuth(CLIENT_PROXY_AUTH_USER, CLIENT_PROXY_AUTH_PASSWORD) + + content, response_code = self.perform_url_call(url, auth=auth) + body = json.loads(content) + if response_code != 200: + return JsonResponse({ + "status_code": response_code, + "content": body, + }) + resp = JsonResponse(body) + return resp diff --git a/templates/map/client/config.json b/templates/map/client/config.json index 805f7e3f..ebe2c971 100644 --- a/templates/map/client/config.json +++ b/templates/map/client/config.json @@ -29,6 +29,7 @@ { "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": "WFS", "title": "Flurstücke (WFS)", "url": "/client/proxy/wfs?", "name": "ave:Flurstueck"}, { "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" }, From 176b8fe504e052c5fc7366fe0bc3bfa3dd5afadb Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Mon, 13 Feb 2023 10:55:58 +0100 Subject: [PATCH 6/6] # Server proxy for client parcel wfs * refactors map_proxy.py * adds proxy support for parcel wfs --- konova/views/map_proxy.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/konova/views/map_proxy.py b/konova/views/map_proxy.py index 9aba1cdd..8f1c9254 100644 --- a/konova/views/map_proxy.py +++ b/konova/views/map_proxy.py @@ -80,6 +80,12 @@ class ClientProxyParcelWFS(BaseClientProxyView): content, response_code = self.perform_url_call(url, auth=auth) body = json.loads(content) + body["crs"] = { + "type": "name", + "properties": { + "name": "urn:ogc:def:crs:EPSG::25832" + } + } if response_code != 200: return JsonResponse({ "status_code": response_code,