From 865a3a51fee4647cf68a5fccc437a26e992468d6 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Tue, 15 Aug 2023 11:29:38 +0200 Subject: [PATCH] Class based views * refactors method based views for parcel fetching, home and logout to class based --- .gitignore | 4 +- konova/urls.py | 16 ++-- konova/views/geometry.py | 156 ++++++++++++++++++++------------------- konova/views/home.py | 103 +++++++++++++------------- konova/views/logout.py | 22 +++--- 5 files changed, 156 insertions(+), 145 deletions(-) diff --git a/.gitignore b/.gitignore index 1e56d1d8..5599d7b7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ # Project exclude paths /venv/ -/.idea/ \ No newline at end of file +/.idea/ +/.coverage +/htmlcov/ diff --git a/konova/urls.py b/konova/urls.py index a3672d47..260251ad 100644 --- a/konova/urls.py +++ b/konova/urls.py @@ -19,17 +19,17 @@ from django.urls import path, include from konova.settings import SSO_SERVER, SSO_PUBLIC_KEY, SSO_PRIVATE_KEY, DEBUG 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.logout import LogoutView +from konova.views.geometry import GeomParcelsView, GeomParcelsContentView +from konova.views.home import HomeView from konova.views.map_proxy import ClientProxyParcelSearch, ClientProxyParcelWFS sso_client = KonovaSSOClient(SSO_SERVER, SSO_PUBLIC_KEY, SSO_PRIVATE_KEY) urlpatterns = [ path('admin/', admin.site.urls), path('login/', include(sso_client.get_urls())), - path('logout/', logout_view, name="logout"), - path('', home_view, name="home"), + path('logout/', LogoutView.as_view(), name="logout"), + path('', HomeView.as_view(), name="home"), path('intervention/', include("intervention.urls")), path('compensation/', include("compensation.urls")), path('ema/', include("ema.urls")), @@ -38,8 +38,8 @@ urlpatterns = [ path('cl/', include("codelist.urls")), path('analysis/', include("analysis.urls")), 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('geom//parcels/', GeomParcelsView.as_view(), name="geometry-parcels"), + path('geom//parcels/', GeomParcelsContentView.as_view(), name="geometry-parcels-content"), path('client/proxy', ClientProxyParcelSearch.as_view(), name="client-proxy-search"), path('client/proxy/wfs', ClientProxyParcelWFS.as_view(), name="client-proxy-wfs"), ] @@ -50,4 +50,4 @@ if DEBUG: ] handler404 = "konova.views.error.get_404_view" -handler500 = "konova.views.error.get_500_view" \ No newline at end of file +handler500 = "konova.views.error.get_500_view" diff --git a/konova/views/geometry.py b/konova/views/geometry.py index f22b9c15..bf34e612 100644 --- a/konova/views/geometry.py +++ b/konova/views/geometry.py @@ -5,104 +5,110 @@ Contact: ksp-servicestelle@sgdnord.rlp.de Created on: 19.08.22 """ +from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.gis.geos import MultiPolygon from django.http import HttpResponse, HttpRequest from django.shortcuts import get_object_or_404 from django.template.loader import render_to_string +from django.views import View -from konova.models import Geometry, Municipal +from konova.models import Geometry from konova.sub_settings.lanis_settings import DEFAULT_SRID_RLP -def get_geom_parcels(request: HttpRequest, id: str): - """ Getter for HTMX +class GeomParcelsView(LoginRequiredMixin, View): - Returns all parcels of the requested geometry rendered into a simple HTML table + def get(self, request: HttpRequest, id: str): + """ Getter for HTMX - Args: - request (HttpRequest): The incoming request - id (str): The geometry's id + Returns all parcels of the requested geometry rendered into a simple HTML table - Returns: - A rendered piece of HTML - """ - # 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_frame.html" - geom = get_object_or_404(Geometry, id=id) - parcels = geom.get_underlying_parcels() - geos_geom = geom.geom or MultiPolygon(srid=DEFAULT_SRID_RLP) + Args: + request (HttpRequest): The incoming request + id (str): The geometry's id - geometry_exists = not geos_geom.empty - parcels_are_currently_calculated = geometry_exists and geos_geom.area > 0 and len(parcels) == 0 - parcels_available = len(parcels) > 0 + Returns: + A rendered piece of HTML + """ + # 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_frame.html" + geom = get_object_or_404(Geometry, id=id) + parcels = geom.get_underlying_parcels() + geos_geom = geom.geom or MultiPolygon(srid=DEFAULT_SRID_RLP) - if parcels_are_currently_calculated: - # Parcels are being calculated right now. Change the status code, so polling stays active for fetching - # resutls after the calculation - status_code = 200 + geometry_exists = not geos_geom.empty + parcels_are_currently_calculated = geometry_exists and geos_geom.area > 0 and len(parcels) == 0 + parcels_available = len(parcels) > 0 - if parcels_available or not geometry_exists: - municipals = geom.get_underlying_municipals(parcels) + if parcels_are_currently_calculated: + # Parcels are being calculated right now. Change the status code, so polling stays active for fetching + # resutls after the calculation + status_code = 200 + if parcels_available or not geometry_exists: + municipals = geom.get_underlying_municipals(parcels) + + rpp = 100 + num_all_parcels = parcels.count() + parcels = parcels[:rpp] + next_page = 1 + if len(parcels) < rpp: + next_page = None + + context = { + "num_parcels": num_all_parcels, + "parcels": parcels, + "municipals": municipals, + "geom_id": str(id), + "next_page": next_page, + } + html = render_to_string(template, context, request) + return HttpResponse(html, status=status_code) + else: + return HttpResponse(None, status=404) + + +class GeomParcelsContentView(LoginRequiredMixin, View): + + def get(self, 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 - num_all_parcels = parcels.count() - parcels = parcels[:rpp] - next_page = 1 + 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 = { - "num_parcels": num_all_parcels, "parcels": parcels, - "municipals": municipals, "geom_id": str(id), "next_page": next_page, } html = render_to_string(template, context, request) return HttpResponse(html, status=status_code) - else: - return HttpResponse(None, status=404) - - -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) diff --git a/konova/views/home.py b/konova/views/home.py index 743ef32a..5253bbfe 100644 --- a/konova/views/home.py +++ b/konova/views/home.py @@ -5,12 +5,13 @@ Contact: ksp-servicestelle@sgdnord.rlp.de Created on: 19.08.22 """ -from django.contrib.auth.decorators import login_required +from django.contrib.auth.mixins import LoginRequiredMixin from django.db.models import Q from django.http import HttpRequest from django.shortcuts import render -from django.utils import timezone +from django.utils.decorators import method_decorator from django.utils.translation import gettext_lazy as _ +from django.views import View from compensation.models import EcoAccount, Compensation from intervention.models import Intervention @@ -20,59 +21,59 @@ from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER from news.models import ServerMessage -@login_required -@any_group_check -def home_view(request: HttpRequest): - """ - Renders the landing page +class HomeView(LoginRequiredMixin, View): - Args: - request (HttpRequest): The used request object + @method_decorator(any_group_check) + def get(self, request: HttpRequest): + """ + Renders the landing page - Returns: - A redirect - """ - template = "konova/home.html" - now = timezone.now() - user = request.user - user_teams = user.shared_teams + Args: + request (HttpRequest): The used request object - # Fetch the four newest active and published ServerMessages - msgs = ServerMessage.get_current_news()[:3] + Returns: + A redirect + """ + template = "konova/home.html" + user = request.user + user_teams = user.shared_teams - # First fetch all valid objects (undeleted, only newest versions) - interventions = Intervention.objects.filter( - deleted=None, - ) - # Then fetch only user related ones - user_interventions = interventions.filter( - Q(users__in=[user]) | Q(teams__in=user_teams) - ).distinct() + # Fetch the four newest active and published ServerMessages + msgs = ServerMessage.get_current_news()[:3] - # Repeat for other objects - comps = Compensation.objects.filter( - deleted=None, - ) - user_comps = comps.filter( - Q(intervention__users__in=[user]) | Q(intervention__teams__in=user_teams) - ).distinct() - eco_accs = EcoAccount.objects.filter( - deleted=None, - ) - user_ecco_accs = eco_accs.filter( - Q(users__in=[user]) | Q(teams__in=user_teams) - ).distinct() + # First fetch all valid objects (undeleted, only newest versions) + interventions = Intervention.objects.filter( + deleted=None, + ) + # Then fetch only user related ones + user_interventions = interventions.filter( + Q(users__in=[user]) | Q(teams__in=user_teams) + ).distinct() - additional_context = { - "msgs": msgs, - "total_intervention_count": interventions.count(), - "user_intervention_count": user_interventions.count(), - "total_compensation_count": comps.count(), - "user_compensation_count": user_comps.count(), - "total_eco_count": eco_accs.count(), - "user_eco_count": user_ecco_accs.count(), - TAB_TITLE_IDENTIFIER: _("Home"), - } - context = BaseContext(request, additional_context).context - return render(request, template, context) + # Repeat for other objects + comps = Compensation.objects.filter( + deleted=None, + ) + user_comps = comps.filter( + Q(intervention__users__in=[user]) | Q(intervention__teams__in=user_teams) + ).distinct() + eco_accs = EcoAccount.objects.filter( + deleted=None, + ) + user_ecco_accs = eco_accs.filter( + Q(users__in=[user]) | Q(teams__in=user_teams) + ).distinct() + + additional_context = { + "msgs": msgs, + "total_intervention_count": interventions.count(), + "user_intervention_count": user_interventions.count(), + "total_compensation_count": comps.count(), + "user_compensation_count": user_comps.count(), + "total_eco_count": eco_accs.count(), + "user_eco_count": user_ecco_accs.count(), + TAB_TITLE_IDENTIFIER: _("Home"), + } + context = BaseContext(request, additional_context).context + return render(request, template, context) diff --git a/konova/views/logout.py b/konova/views/logout.py index 943673d0..fe4d0db4 100644 --- a/konova/views/logout.py +++ b/konova/views/logout.py @@ -8,19 +8,21 @@ Created on: 19.08.22 from django.contrib.auth import logout from django.http import HttpRequest from django.shortcuts import redirect +from django.views import View from konova.sub_settings.sso_settings import SSO_SERVER_BASE -def logout_view(request: HttpRequest): - """ - Logout route for ending the session manually. +class LogoutView(View): + def get(self, request: HttpRequest): + """ + Logout route for ending the session manually. - Args: - request (HttpRequest): The used request object + Args: + request (HttpRequest): The used request object - Returns: - A redirect - """ - logout(request) - return redirect(SSO_SERVER_BASE) + Returns: + A redirect + """ + logout(request) + return redirect(SSO_SERVER_BASE)