diff --git a/compensation/filters.py b/compensation/filters.py index 8bf0c232..d028e3ce 100644 --- a/compensation/filters.py +++ b/compensation/filters.py @@ -8,20 +8,45 @@ Created on: 29.07.21 import django_filters from django.utils.translation import gettext_lazy as _ from django import forms -from django.db.models import QuerySet +from django.db.models import QuerySet, Q -from intervention.filters import InterventionTableFilter +from konova.filters.mixins import ConservationOfficeTableFilterMixin +from konova.filters.table_filters import QueryTableFilter, CheckboxTableFilter, SelectionTableFilter, AbstractTableFilter -class CompensationTableFilter(InterventionTableFilter): - """ TableFilter for compensations +class SelectionCompensationTableFilter(SelectionTableFilter): + """ Specialization of regular SelectionTableFilter for compensation model - Based widely on InterventionTableFilter. - Just some minor changes for Compensation model. + """ + def filter_reg_office(self, queryset, name, value): + return queryset.filter( + intervention__responsible__registration_office=value + ) + + def filter_cons_office(self, queryset, name, value): + return queryset.filter( + intervention__responsible__conservation_office=value + ) + + +class QueryCompensationTableFilter(QueryTableFilter): + """ Specialization of regular QueryTableFilter for compensation model + + """ + def filter_file_number(self, queryset, name, value) -> QuerySet: + queryset = queryset.filter( + Q(intervention__responsible__registration_file_number__icontains=value) | + Q(intervention__responsible__conservation_file_number__icontains=value) + ) + return queryset + + +class CheckboxCompensationTableFilter(CheckboxTableFilter): + """ Specialization of regular CheckboxTableFilter for compensation model """ - def _filter_show_all(self, queryset, name, value) -> QuerySet: + def filter_show_all(self, queryset, name, value) -> QuerySet: """ Filters queryset depending on value of 'show_all' setting Args: @@ -39,7 +64,7 @@ class CompensationTableFilter(InterventionTableFilter): else: return queryset - def _filter_show_recorded(self, queryset, name, value) -> QuerySet: + def filter_show_recorded(self, queryset, name, value) -> QuerySet: """ Filters queryset depending on value of 'show_recorded' setting Args: @@ -58,21 +83,51 @@ class CompensationTableFilter(InterventionTableFilter): return queryset -class EcoAccountTableFilter(InterventionTableFilter): - """ TableFilter for eco accounts +class CompensationTableFilter(AbstractTableFilter): + """ TableFilter for compensations Based widely on InterventionTableFilter. - Just some minor changes for EcoAccount model. + Just some minor changes for Compensation model. """ + def __init__(self, user=None, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.user = user + qs = kwargs.get("queryset", None) + request_data = kwargs.get("data", None) + + # Overwrite all filters for special needs of compensations + self.selection_filter = SelectionCompensationTableFilter( + data=request_data, + queryset=qs, + ) + self.query_filter = QueryCompensationTableFilter( + data=request_data, + queryset=self.selection_filter.qs, + ) + self.checkbox_filter = CheckboxCompensationTableFilter( + user=user, + data=request_data, + queryset=self.query_filter.qs, + ) + # Overwrite final queryset as well + self.qs = self.checkbox_filter.qs + + +class CheckboxEcoAccountTableFilter(CheckboxTableFilter): sr = django_filters.BooleanFilter( - method='_filter_only_show_unrecorded', + method='filter_only_show_unrecorded', label=_("Show only unrecorded"), label_suffix=_(""), - widget=forms.CheckboxInput() + widget=forms.CheckboxInput( + attrs={ + "class": "form-check-input", + } + ) ) - def _filter_show_all(self, queryset, name, value) -> QuerySet: + def filter_show_all(self, queryset, name, value) -> QuerySet: """ Filters queryset depending on value of 'show_all' setting Args: @@ -90,7 +145,7 @@ class EcoAccountTableFilter(InterventionTableFilter): else: return queryset - def _filter_only_show_unrecorded(self, queryset, name, value) -> QuerySet: + def filter_only_show_unrecorded(self, queryset, name, value) -> QuerySet: """ Filters queryset depending on value of 'show_recorded' setting Args: @@ -107,3 +162,40 @@ class EcoAccountTableFilter(InterventionTableFilter): ) else: return queryset + + +class SelectionEcoAccountTableFilter(ConservationOfficeTableFilterMixin): + """ Special selection table filter for eco accounts + + EcoAccounts only need a selection filter for conservation office + + """ + pass + + +class EcoAccountTableFilter(AbstractTableFilter): + """ TableFilter for eco accounts + + """ + def __init__(self, user=None, *args, **kwargs): + super().__init__(*args, **kwargs) + self.user = user + qs = kwargs.get("queryset", None) + request_data = kwargs.get("data", None) + + # Pipe the queryset through all needed filters + self.selection_filter = SelectionEcoAccountTableFilter( + data=request_data, + queryset=qs, + ) + self.query_filter = QueryTableFilter( + data=request_data, + queryset=self.selection_filter.qs, + ) + self.checkbox_filter = CheckboxEcoAccountTableFilter( + user=user, + data=request_data, + queryset=self.query_filter.qs, + ) + # Overwrite the final queryset result + self.qs = self.checkbox_filter.qs diff --git a/compensation/tables.py b/compensation/tables.py index df7ec3eb..6e6a190b 100644 --- a/compensation/tables.py +++ b/compensation/tables.py @@ -16,11 +16,11 @@ from django.utils.translation import gettext_lazy as _ from compensation.filters import CompensationTableFilter, EcoAccountTableFilter from compensation.models import Compensation, EcoAccount from konova.sub_settings.django_settings import DEFAULT_DATE_TIME_FORMAT -from konova.utils.tables import BaseTable +from konova.utils.tables import BaseTable, TableRenderMixin import django_tables2 as tables -class CompensationTable(BaseTable): +class CompensationTable(BaseTable, TableRenderMixin): id = tables.Column( verbose_name=_("Identifier"), orderable=True, @@ -67,7 +67,8 @@ class CompensationTable(BaseTable): data=request.GET, queryset=qs, ) - super().__init__(request, self.filter, *args, **kwargs) + kwargs["queryset"] = self.filter.qs + super().__init__(request, *args, **kwargs) def render_id(self, value, record: Compensation): """ Renders the id column for a compensation @@ -161,7 +162,7 @@ class CompensationTable(BaseTable): return format_html(html) -class EcoAccountTable(BaseTable): +class EcoAccountTable(BaseTable, TableRenderMixin): id = tables.Column( verbose_name=_("Identifier"), orderable=True, @@ -207,7 +208,8 @@ class EcoAccountTable(BaseTable): data=request.GET, queryset=qs, ) - super().__init__(request, self.filter, *args, **kwargs) + kwargs["queryset"] = self.filter.qs + super().__init__(request, *args, **kwargs) def render_id(self, value, record: EcoAccount): """ Renders the id column for an eco account diff --git a/ema/filters.py b/ema/filters.py index 2817d6dc..70b93da0 100644 --- a/ema/filters.py +++ b/ema/filters.py @@ -5,8 +5,6 @@ Contact: michel.peltriaux@sgdnord.rlp.de Created on: 19.08.21 """ -from django.db.models import QuerySet - from compensation.filters import EcoAccountTableFilter diff --git a/ema/tables.py b/ema/tables.py index 9055059d..d30f3e36 100644 --- a/ema/tables.py +++ b/ema/tables.py @@ -14,12 +14,12 @@ from django.urls import reverse import django_tables2 as tables from konova.sub_settings.django_settings import DEFAULT_DATE_TIME_FORMAT -from konova.utils.tables import BaseTable +from konova.utils.tables import BaseTable, TableRenderMixin from ema.filters import EmaTableFilter from ema.models import Ema -class EmaTable(BaseTable): +class EmaTable(BaseTable, TableRenderMixin): """ Since EMA and compensation are basically the same, we can reuse CompensationTableFilter and extend the EMA filter in the future by inheriting. @@ -65,7 +65,8 @@ class EmaTable(BaseTable): data=request.GET, queryset=qs, ) - super().__init__(request, self.filter, *args, **kwargs) + kwargs["queryset"] = self.filter.qs + super().__init__(request, *args, **kwargs) def render_id(self, value, record: Ema): """ Renders the id column for a EMA diff --git a/intervention/filters.py b/intervention/filters.py index 205b35c6..976c9767 100644 --- a/intervention/filters.py +++ b/intervention/filters.py @@ -5,119 +5,29 @@ Contact: michel.peltriaux@sgdnord.rlp.de Created on: 22.07.21 """ -import django_filters -from django import forms -from django.contrib.auth.models import User -from django.db.models import QuerySet, Q - -from django.utils.translation import gettext_lazy as _ - -from intervention.inputs import DummyFilterInput -from intervention.models import Intervention +from konova.filters.table_filters import AbstractTableFilter, SelectionTableFilter, QueryTableFilter, CheckboxTableFilter -class InterventionTableFilter(django_filters.FilterSet): - """ TableFilter for Intervention model - - """ - sa = django_filters.BooleanFilter( - method='_filter_show_all', - label=_("Show unshared"), - label_suffix=_(""), - widget=forms.CheckboxInput() - ) - q = django_filters.Filter( - method='_filter_by_keyword', - # Since we use a custom search bar in the template, we need to 'render' this filter - # as 'anonymous' HiddenInput (no id, no name). This way our custom search bar's id and name won't be - # overwritten with these id and name (which would be equal) - # This way we can use the simple filter method mapping for a parameter without using a predefined widget! - widget=DummyFilterInput(), - ) - sr = django_filters.BooleanFilter( - method='_filter_show_recorded', - label=_("Show recorded"), - label_suffix=_(""), - widget=forms.CheckboxInput() - ) - # Gemarkung ##ToDo - g = django_filters.CharFilter( - field_name="name", - lookup_expr="icontains", - label=_(""), - label_suffix=_(""), - widget=forms.TextInput( - attrs={ - "placeholder": _("District"), - "title": _("Search for district") - } - ), - ) - # Kreis - ## ToDo - # Flur - ## ToDo - # Zähler - ## ToDo - # Nenner - ## ToDo - - class Meta: - model = Intervention - fields = [] - - def __init__(self, user: User, *args, **kwargs): - self.user = user +class InterventionTableFilter(AbstractTableFilter): + def __init__(self, user=None, *args, **kwargs): super().__init__(*args, **kwargs) + self.user = user + qs = kwargs.get("queryset", None) + request_data = kwargs.get("data", None) - def _filter_by_keyword(self, queryset, name, value) -> QuerySet: - """ Filters queryset depending on value of search bar input + # Pipe the queryset through all needed filters + self.selection_filter = SelectionTableFilter( + data=request_data, + queryset=qs, + ) + self.query_filter = QueryTableFilter( + data=request_data, + queryset=self.selection_filter.qs, + ) + self.checkbox_filter = CheckboxTableFilter( + user=user, + data=request_data, + queryset=self.query_filter.qs, + ) + self.qs = self.checkbox_filter.qs - Args: - queryset (): - name (): - value (): - - Returns: - - """ - value = value.strip() - # build filter expression - q = Q(title__icontains=value) | Q(identifier__icontains=value) - return queryset.filter(q) - - def _filter_show_all(self, queryset, name, value) -> QuerySet: - """ Filters queryset depending on value of 'show_all' setting - - Args: - queryset (): - name (): - value (): - - Returns: - - """ - if not value: - return queryset.filter( - users__in=[self.user], # requesting user has access - ) - else: - return queryset - - def _filter_show_recorded(self, queryset, name, value) -> QuerySet: - """ Filters queryset depending on value of 'show_recorded' setting - - Args: - queryset (): - name (): - value (): - - Returns: - - """ - if not value: - return queryset.filter( - recorded=None, - ) - else: - return queryset diff --git a/intervention/tables.py b/intervention/tables.py index 103d419a..0ed003fa 100644 --- a/intervention/tables.py +++ b/intervention/tables.py @@ -14,11 +14,11 @@ from django.utils.translation import gettext_lazy as _ from intervention.filters import InterventionTableFilter from intervention.models import Intervention from konova.sub_settings.django_settings import DEFAULT_DATE_TIME_FORMAT, DEFAULT_DATE_FORMAT -from konova.utils.tables import BaseTable +from konova.utils.tables import BaseTable, TableRenderMixin import django_tables2 as tables -class InterventionTable(BaseTable): +class InterventionTable(BaseTable, TableRenderMixin): id = tables.Column( verbose_name=_("Identifier"), orderable=True, @@ -71,7 +71,8 @@ class InterventionTable(BaseTable): data=request.GET, queryset=qs, ) - super().__init__(request, self.filter, *args, **kwargs) + kwargs["queryset"] = self.filter.qs + super().__init__(request, *args, **kwargs) def render_id(self, value, record: Intervention): """ Renders the id column for an intervention diff --git a/konova/filters/__init__.py b/konova/filters/__init__.py new file mode 100644 index 00000000..0529e022 --- /dev/null +++ b/konova/filters/__init__.py @@ -0,0 +1,7 @@ +""" +Author: Michel Peltriaux +Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany +Contact: michel.peltriaux@sgdnord.rlp.de +Created on: 12.01.22 + +""" diff --git a/konova/filters/mixins.py b/konova/filters/mixins.py new file mode 100644 index 00000000..5625ff36 --- /dev/null +++ b/konova/filters/mixins.py @@ -0,0 +1,406 @@ +""" +Author: Michel Peltriaux +Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany +Contact: michel.peltriaux@sgdnord.rlp.de +Created on: 12.01.22 + +""" +import django_filters +from django import forms +from django.db.models import QuerySet, Q +from django.utils.translation import gettext_lazy as _ +from dal_select2.widgets import ModelSelect2 + +from codelist.models import KonovaCode +from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID, CODELIST_REGISTRATION_OFFICE_ID +from intervention.inputs import DummyFilterInput +from konova.models import Parcel, District + + +class KeywordTableFilterMixin(django_filters.FilterSet): + q = django_filters.Filter( + method='filter_by_keyword', + # Since we use a custom search bar in the template, we need to 'render' this filter + # as 'anonymous' HiddenInput (no id, no name). This way our custom search bar's id and name won't be + # overwritten with these id and name (which would be equal) + # This way we can use the simple filter method mapping for a parameter without using a predefined widget! + widget=DummyFilterInput(), + ) + + class Meta: + abstract = True + + def filter_by_keyword(self, queryset, name, value) -> QuerySet: + """ Filters queryset depending on value of search bar input + + Args: + queryset (): + name (): + value (): + + Returns: + + """ + value = value.strip() + # build filter expression + q = Q(title__icontains=value) | Q(identifier__icontains=value) + return queryset.filter(q) + + +class FileNumberTableFilterMixin(django_filters.FilterSet): + rf = django_filters.CharFilter( + method="filter_file_number", + label=_(""), + label_suffix=_(""), + widget=forms.TextInput( + attrs={ + "placeholder": _("File number"), + "title": _("Search for file number"), + "class": "form-control", + } + ), + ) + + def filter_file_number(self, queryset, name, value) -> QuerySet: + queryset = queryset.filter( + Q(responsible__registration_file_number__icontains=value) | + Q(responsible__conservation_file_number__icontains=value) + ) + return queryset + + +class GeoReferencedTableFilterMixin(django_filters.FilterSet): + """ A mixin for AbstractTableFilter + + Specialized on filtering GeoReferenced model types + + """ + # Parcel gmrkng + di = django_filters.CharFilter( + method="filter_district", + label=_(""), + label_suffix=_(""), + widget=forms.TextInput( + attrs={ + "placeholder": _("District"), + "title": _("Search for district"), + "class": "form-control", + } + ), + ) + # Parcel gmrkng + pg = django_filters.CharFilter( + method="filter_gmrkng", + label=_(""), + label_suffix=_(""), + widget=forms.TextInput( + attrs={ + "placeholder": _("Parcel gmrkng"), + "title": _("Search for parcel gmrkng"), + "class": "form-control", + } + ), + ) + # Parcel + p = django_filters.CharFilter( + method="filter_parcel", + label=_(""), + label_suffix=_(""), + widget=forms.TextInput( + attrs={ + "placeholder": _("Parcel"), + "title": _("Search for parcel"), + "class": "form-control", + } + ), + ) + # Parcel counter + pc = django_filters.CharFilter( + method="filter_parcel_counter", + label=_(""), + label_suffix=_(""), + widget=forms.TextInput( + attrs={ + "placeholder": _("Parcel counter"), + "title": _("Search for parcel counter"), + "class": "form-control", + } + ), + ) + + # Parcel counter + pn = django_filters.CharFilter( + method="filter_parcel_number", + label=_(""), + label_suffix=_(""), + widget=forms.TextInput( + attrs={ + "placeholder": _("Parcel number"), + "title": _("Search for parcel number"), + "class": "form-control", + } + ), + ) + + class Meta: + abstract = True + + def _filter_parcel_reference(self, queryset, name, value, filter_value) -> QuerySet: + """ Filters the parcel entries by a given filter_value. + + filter_value may already include further filter annotations like 'xy__icontains' + + Args: + queryset (): + name (): + value (): + filter_value (): + + Returns: + + """ + _filter = { + filter_value: value + } + matching_parcels = Parcel.objects.filter( + **_filter + ) + related_geoms = matching_parcels.values( + "geometries" + ).distinct() + queryset = queryset.filter( + geometry__id__in=related_geoms + ) + return queryset + + def filter_district(self, queryset, name, value) -> QuerySet: + """ Filters queryset depending on value for 'Gemarkung' + + Args: + queryset (): + name (): + value (): + + Returns: + + """ + matching_districts = District.objects.filter( + krs=value + ) + matching_parcels = Parcel.objects.filter( + district__in=matching_districts + ) + related_geoms = matching_parcels.values( + "geometries" + ).distinct() + queryset = queryset.filter( + geometry__id__in=related_geoms + ) + return queryset + + def filter_gmrkng(self, queryset, name, value) -> QuerySet: + """ Filters queryset depending on value for 'Gemarkung' + + Args: + queryset (): + name (): + value (): + + Returns: + + """ + queryset = self._filter_parcel_reference(queryset, name, value, "gmrkng__istartswith") + return queryset + + def filter_parcel(self, queryset, name, value) -> QuerySet: + """ Filters queryset depending on value for 'Parcel' + + Args: + queryset (): + name (): + value (): + + Returns: + + """ + value = value.replace("-", "") + queryset = self._filter_parcel_reference(queryset, name, value, "flr") + return queryset + + def filter_parcel_counter(self, queryset, name, value) -> QuerySet: + """ Filters queryset depending on value for 'Parcel' + + Args: + queryset (): + name (): + value (): + + Returns: + + """ + value = value.replace("-", "") + queryset = self._filter_parcel_reference(queryset, name, value, "flrstck_zhlr") + return queryset + + def filter_parcel_number(self, queryset, name, value) -> QuerySet: + """ Filters queryset depending on value for 'Parcel' + + Args: + queryset (): + name (): + value (): + + Returns: + + """ + value = value.replace("-", "") + queryset = self._filter_parcel_reference(queryset, name, value, "flrstck_nnr") + return queryset + + +class ShareableTableFilterMixin(django_filters.FilterSet): + """ A mixin for AbstractTableFilter + + Specialized on filtering shareable model types + + """ + sa = django_filters.BooleanFilter( + method='filter_show_all', + label=_("Show unshared"), + label_suffix=_(""), + widget=forms.CheckboxInput( + attrs={ + "class": "form-check-input", + } + ) + ) + + class Meta: + abstract = True + + def __init__(self, *args, **kwargs): + self.user = kwargs.pop("user", None) + if self.user is None: + raise AttributeError("User must be set for further filtering!") + super().__init__(*args, **kwargs) + + def filter_show_all(self, queryset, name, value) -> QuerySet: + """ Filters queryset depending on value of 'show_all' setting + + Args: + queryset (): + name (): + value (): + + Returns: + + """ + if not value: + return queryset.filter( + users__in=[self.user], # requesting user has access + ) + else: + return queryset + + +class RecordableTableFilterMixin(django_filters.FilterSet): + """ A mixin for AbstractTableFilter + + Specialized on filtering recordable model types + + """ + sr = django_filters.BooleanFilter( + method='filter_show_recorded', + label=_("Show recorded"), + label_suffix=_(""), + widget=forms.CheckboxInput( + attrs={ + "class": "form-check-input", + } + ) + ) + + class Meta: + abstract = True + + def filter_show_recorded(self, queryset, name, value) -> QuerySet: + """ Filters queryset depending on value of 'show_recorded' setting + + Args: + queryset (): + name (): + value (): + + Returns: + + """ + if not value: + return queryset.filter( + recorded=None, + ) + else: + return queryset + + +class RegistrationOfficeTableFilterMixin(django_filters.FilterSet): + """ A mixin for AbstractTableFilter + + Specialized on filtering for related registration offices + + """ + ro = django_filters.ModelChoiceFilter( + method="filter_reg_office", + label=_(""), + label_suffix=_(""), + queryset=KonovaCode.objects.filter( + is_archived=False, + is_leaf=True, + code_lists__in=[CODELIST_REGISTRATION_OFFICE_ID], + ), + widget=ModelSelect2( + url="codes-registration-office-autocomplete", + attrs={ + "data-placeholder": _("Registration office"), + "title": _("Search for registration office"), + "class": "", + } + ), + ) + + def filter_reg_office(self, queryset, name, value): + qs = queryset.filter( + responsible__registration_office=value + ) + return qs + + +class ConservationOfficeTableFilterMixin(django_filters.FilterSet): + """ A mixin for AbstractTableFilter + + Specialized on filtering for related conservation offices + + """ + co = django_filters.ModelChoiceFilter( + method="filter_cons_office", + label=_(""), + label_suffix=_(""), + queryset=KonovaCode.objects.filter( + is_archived=False, + is_leaf=True, + code_lists__in=[CODELIST_CONSERVATION_OFFICE_ID], + ), + widget=ModelSelect2( + url="codes-conservation-office-autocomplete", + attrs={ + "data-placeholder": _("Conservation office"), + "title": _("Search for conservation office"), + "class": "", + } + ), + ) + + def filter_cons_office(self, queryset, name, value): + qs = queryset.filter( + responsible__conservation_office=value + ) + return qs diff --git a/konova/filters/table_filters.py b/konova/filters/table_filters.py new file mode 100644 index 00000000..8dca6d5f --- /dev/null +++ b/konova/filters/table_filters.py @@ -0,0 +1,50 @@ +""" +Author: Michel Peltriaux +Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany +Contact: michel.peltriaux@sgdnord.rlp.de +Created on: 12.01.22 + +""" +import django_filters + +from konova.filters.mixins import RegistrationOfficeTableFilterMixin, ConservationOfficeTableFilterMixin, \ + KeywordTableFilterMixin, FileNumberTableFilterMixin, GeoReferencedTableFilterMixin, ShareableTableFilterMixin, \ + RecordableTableFilterMixin + + +class AbstractTableFilter(django_filters.FilterSet): + """ Base TableFilter for all models + + """ + selection_filter = None + query_filter = None + checkbox_filter = None + qs = None + user = None + + class Meta: + abstract = True + + +class SelectionTableFilter(RegistrationOfficeTableFilterMixin, + ConservationOfficeTableFilterMixin): + """ TableFilter holding different filter options for selection related filtering + + """ + pass + + +class QueryTableFilter(KeywordTableFilterMixin, + FileNumberTableFilterMixin, + GeoReferencedTableFilterMixin): + """ TableFilter holding different filter options for query related filtering + + """ + pass + + +class CheckboxTableFilter(ShareableTableFilterMixin, RecordableTableFilterMixin): + """ TableFilter holding different filter options for checkbox related filtering + + """ + pass \ No newline at end of file diff --git a/konova/models/geometry.py b/konova/models/geometry.py index aad39d6f..0a380b48 100644 --- a/konova/models/geometry.py +++ b/konova/models/geometry.py @@ -110,9 +110,13 @@ class Geometry(BaseResource): underlying_parcels = [] for result in fetched_parcels: fetched_parcel = result[typename] + # There could be parcels which include the word 'Flur', + # which needs to be deleted and just keep the numerical values + ## THIS CAN BE REMOVED IN THE FUTURE, WHEN 'Flur' WON'T OCCUR ANYMORE! + flr_val = fetched_parcel["ave:flur"].replace("Flur ", "") parcel_obj = Parcel.objects.get_or_create( gmrkng=fetched_parcel["ave:gemarkung"], - flr=fetched_parcel["ave:flur"], + flr=flr_val, flrstck_nnr=fetched_parcel['ave:flstnrnen'], flrstck_zhlr=fetched_parcel['ave:flstnrzae'], )[0] diff --git a/konova/tests/test_views.py b/konova/tests/test_views.py index d31726ff..dad44297 100644 --- a/konova/tests/test_views.py +++ b/konova/tests/test_views.py @@ -404,6 +404,7 @@ class AutocompleteTestCase(BaseViewTestCase): cls.atcmplt_code_comp_process = reverse("codes-process-type-autocomplete") cls.atcmplt_code_comp_reg_off = reverse("codes-registration-office-autocomplete") cls.atcmplt_code_comp_cons_off = reverse("codes-conservation-office-autocomplete") + cls.atcmplt_code_share_user = reverse("share-user-autocomplete") def _test_views_anonymous_user(self): # ATTENTION: As of the current state of django-autocomplete-light, there is no way to check on authenticated @@ -421,6 +422,7 @@ class AutocompleteTestCase(BaseViewTestCase): self.atcmplt_code_comp_process, self.atcmplt_code_comp_reg_off, self.atcmplt_code_comp_cons_off, + self.atcmplt_code_share_user, ] self.assert_url_fail(client, urls) diff --git a/konova/utils/tables.py b/konova/utils/tables.py index 9d0329f3..d3a0406b 100644 --- a/konova/utils/tables.py +++ b/konova/utils/tables.py @@ -5,15 +5,13 @@ Contact: michel.peltriaux@sgdnord.rlp.de Created on: 25.11.20 """ -import uuid -from django import forms from django.core.paginator import PageNotAnInteger, EmptyPage from django.http import HttpRequest from django.utils.html import format_html import django_tables2 as tables -from konova.forms import BaseForm +from konova.models import BaseObject from konova.settings import PAGE_SIZE_DEFAULT, PAGE_PARAM, RESULTS_PER_PAGE_PARAM, PAGE_SIZE_OPTIONS @@ -31,10 +29,8 @@ class BaseTable(tables.tables.Table): "class": "table table-hover table-responsive-md table-responsive-sm", } - def __init__(self, request: HttpRequest = None, filter_set=None, queryset=None, *args, **kwargs): + def __init__(self, request: HttpRequest = None, queryset=None, *args, **kwargs): self.user = request.user or None - if filter_set is not None: - queryset = filter_set.qs kwargs["data"] = queryset kwargs["request"] = request super().__init__(*args, **kwargs) @@ -149,22 +145,21 @@ class BaseTable(tables.tables.Table): ) -class ChoicesColumnForm(BaseForm): - select = forms.ChoiceField( - choices=[], - label="", - label_suffix="", - widget=forms.Select( - attrs={ - "onchange": "submit();", - } - ) - ) +class TableRenderMixin: + """ Holds different render methods for general purposes - def __init__(self, *args, **kwargs): - self.action_url = kwargs.pop("action_url", None) - self.choices = kwargs.pop("choices", []) - super().__init__(*args, **kwargs) - self.auto_id += "_" + str(uuid.uuid4()) - if len(self.choices) > 0: - self.fields["select"].choices = self.choices + """ + def render_t(self, value, record: BaseObject): + """ Renders a BaseObject title + + Args: + value (): + record (): + + Returns: + + """ + max_length = 75 + if len(value) > max_length: + value = f"{value[:max_length]}..." + return value \ No newline at end of file diff --git a/locale/de/LC_MESSAGES/django.mo b/locale/de/LC_MESSAGES/django.mo index 2edf9a00..1246234f 100644 Binary files a/locale/de/LC_MESSAGES/django.mo and b/locale/de/LC_MESSAGES/django.mo differ diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index a83b6aab..cb6b2a81 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -3,23 +3,26 @@ # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # -#: compensation/filters.py:71 compensation/forms/modalForms.py:34 +#: compensation/filters.py:91 compensation/forms/modalForms.py:34 #: compensation/forms/modalForms.py:45 compensation/forms/modalForms.py:61 #: compensation/forms/modalForms.py:238 compensation/forms/modalForms.py:316 -#: intervention/filters.py:26 intervention/filters.py:40 -#: intervention/filters.py:47 intervention/filters.py:48 #: intervention/forms/forms.py:52 intervention/forms/forms.py:154 #: intervention/forms/forms.py:166 intervention/forms/modalForms.py:125 #: intervention/forms/modalForms.py:138 intervention/forms/modalForms.py:151 -#: konova/forms.py:140 konova/forms.py:241 konova/forms.py:312 -#: konova/forms.py:339 konova/forms.py:349 konova/forms.py:362 -#: konova/forms.py:374 konova/forms.py:392 user/forms.py:38 +#: konova/filters.py:63 konova/filters.py:64 konova/filters.py:91 +#: konova/filters.py:92 konova/filters.py:104 konova/filters.py:105 +#: konova/filters.py:117 konova/filters.py:118 konova/filters.py:130 +#: konova/filters.py:131 konova/filters.py:144 konova/filters.py:145 +#: konova/filters.py:280 konova/filters.py:324 konova/forms.py:140 +#: konova/forms.py:241 konova/forms.py:312 konova/forms.py:339 +#: konova/forms.py:349 konova/forms.py:362 konova/forms.py:374 +#: konova/forms.py:392 user/forms.py:38 #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-01-07 15:32+0100\n" +"POT-Creation-Date: 2022-01-11 17:07+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -145,7 +148,7 @@ msgstr "Geprüft" #: analysis/templates/analysis/reports/includes/intervention/compensated_by.html:9 #: analysis/templates/analysis/reports/includes/intervention/laws.html:20 #: analysis/templates/analysis/reports/includes/old_data/amount.html:18 -#: compensation/tables.py:41 compensation/tables.py:181 +#: compensation/tables.py:41 compensation/tables.py:182 #: compensation/templates/compensation/detail/compensation/view.html:77 #: compensation/templates/compensation/detail/eco_account/includes/deductions.html:31 #: compensation/templates/compensation/detail/eco_account/view.html:44 @@ -231,7 +234,7 @@ msgstr "Kompensationsart" #: analysis/templates/analysis/reports/includes/intervention/compensated_by.html:15 #: analysis/templates/analysis/reports/includes/old_data/amount.html:29 -#: compensation/tables.py:84 +#: compensation/tables.py:85 #: compensation/templates/compensation/detail/compensation/view.html:19 #: konova/templates/konova/home.html:49 templates/navbars/navbar.html:28 msgid "Compensation" @@ -276,14 +279,14 @@ msgstr "Typ" #: analysis/templates/analysis/reports/includes/old_data/amount.html:24 #: intervention/forms/modalForms.py:306 intervention/forms/modalForms.py:313 -#: intervention/tables.py:88 +#: intervention/tables.py:89 #: intervention/templates/intervention/detail/view.html:19 #: konova/templates/konova/home.html:11 templates/navbars/navbar.html:22 msgid "Intervention" msgstr "Eingriff" #: analysis/templates/analysis/reports/includes/old_data/amount.html:34 -#: compensation/tables.py:224 +#: compensation/tables.py:226 #: compensation/templates/compensation/detail/eco_account/view.html:19 #: intervention/forms/modalForms.py:279 intervention/forms/modalForms.py:286 #: konova/templates/konova/home.html:88 templates/navbars/navbar.html:34 @@ -298,12 +301,12 @@ msgstr "Altfälle" msgid "Before" msgstr "Vor" -#: compensation/filters.py:70 +#: compensation/filters.py:90 msgid "Show only unrecorded" msgstr "Nur unverzeichnete anzeigen" #: compensation/forms/forms.py:32 compensation/tables.py:25 -#: compensation/tables.py:166 ema/tables.py:28 intervention/forms/forms.py:26 +#: compensation/tables.py:167 ema/tables.py:28 intervention/forms/forms.py:26 #: intervention/tables.py:23 #: intervention/templates/intervention/detail/includes/compensations.html:30 msgid "Identifier" @@ -314,7 +317,7 @@ msgid "Generated automatically" msgstr "Automatisch generiert" #: compensation/forms/forms.py:44 compensation/tables.py:30 -#: compensation/tables.py:171 +#: compensation/tables.py:172 #: compensation/templates/compensation/detail/compensation/includes/documents.html:28 #: compensation/templates/compensation/detail/compensation/view.html:31 #: compensation/templates/compensation/detail/eco_account/includes/documents.html:28 @@ -633,65 +636,65 @@ msgstr "" "Es wurde bereits mehr Fläche abgebucht, als Sie nun als abbuchbar einstellen " "wollen. Kontaktieren Sie die für die Abbuchungen verantwortlichen Nutzer!" -#: compensation/tables.py:47 compensation/tables.py:187 ema/tables.py:44 +#: compensation/tables.py:47 compensation/tables.py:188 ema/tables.py:44 #: intervention/tables.py:51 msgid "Editable" msgstr "Freigegeben" -#: compensation/tables.py:53 compensation/tables.py:193 ema/tables.py:50 +#: compensation/tables.py:53 compensation/tables.py:194 ema/tables.py:50 #: intervention/tables.py:57 msgid "Last edit" msgstr "Zuletzt bearbeitet" -#: compensation/tables.py:84 compensation/tables.py:224 ema/tables.py:82 -#: intervention/tables.py:88 +#: compensation/tables.py:85 compensation/tables.py:226 ema/tables.py:83 +#: intervention/tables.py:89 msgid "Open {}" msgstr "Öffne {}" -#: compensation/tables.py:105 intervention/tables.py:107 +#: compensation/tables.py:106 intervention/tables.py:108 msgid "Not checked yet" msgstr "Noch nicht geprüft" -#: compensation/tables.py:110 intervention/tables.py:112 +#: compensation/tables.py:111 intervention/tables.py:113 msgid "Checked on {} by {}" msgstr "Am {} von {} geprüft worden" -#: compensation/tables.py:129 +#: compensation/tables.py:130 #: compensation/templates/compensation/detail/compensation/view.html:80 #: compensation/templates/compensation/detail/eco_account/includes/deductions.html:56 #: compensation/templates/compensation/detail/eco_account/view.html:47 -#: ema/tables.py:101 ema/templates/ema/detail/view.html:38 -#: intervention/tables.py:131 +#: ema/tables.py:102 ema/templates/ema/detail/view.html:38 +#: intervention/tables.py:132 #: intervention/templates/intervention/detail/view.html:85 msgid "Not recorded yet" msgstr "Noch nicht verzeichnet" -#: compensation/tables.py:134 compensation/tables.py:262 ema/tables.py:106 -#: intervention/tables.py:136 +#: compensation/tables.py:135 compensation/tables.py:264 ema/tables.py:107 +#: intervention/tables.py:137 msgid "Recorded on {} by {}" msgstr "Am {} von {} verzeichnet worden" -#: compensation/tables.py:158 compensation/tables.py:284 ema/tables.py:129 -#: intervention/tables.py:159 +#: compensation/tables.py:159 compensation/tables.py:286 ema/tables.py:130 +#: intervention/tables.py:160 msgid "Full access granted" msgstr "Für Sie freigegeben - Datensatz kann bearbeitet werden" -#: compensation/tables.py:158 compensation/tables.py:284 ema/tables.py:129 -#: intervention/tables.py:159 +#: compensation/tables.py:159 compensation/tables.py:286 ema/tables.py:130 +#: intervention/tables.py:160 msgid "Access not granted" msgstr "Nicht freigegeben - Datensatz nur lesbar" -#: compensation/tables.py:176 +#: compensation/tables.py:177 #: compensation/templates/compensation/detail/eco_account/view.html:35 #: konova/templates/konova/widgets/progressbar.html:3 msgid "Available" msgstr "Verfügbar" -#: compensation/tables.py:202 +#: compensation/tables.py:203 msgid "Eco Accounts" msgstr "Ökokonten" -#: compensation/tables.py:257 +#: compensation/tables.py:259 msgid "Not recorded yet. Can not be used for deductions, yet." msgstr "" "Noch nicht verzeichnet. Kann noch nicht für Abbuchungen genutzt werden." @@ -1177,7 +1180,7 @@ msgstr "" "Maßnahmen aus Ersatzzahlungen, die nach 2015 rechtskräftig wurden, werden " "durch die Stiftung Natur und Umwelt verwaltet." -#: ema/tables.py:82 templates/navbars/navbar.html:43 +#: ema/tables.py:83 templates/navbars/navbar.html:43 msgid "EMA" msgstr "" @@ -1197,22 +1200,6 @@ msgstr "EMA {} bearbeitet" msgid "EMA removed" msgstr "EMA entfernt" -#: intervention/filters.py:25 -msgid "Show unshared" -msgstr "Nicht freigegebene anzeigen" - -#: intervention/filters.py:39 -msgid "Show recorded" -msgstr "Verzeichnete anzeigen" - -#: intervention/filters.py:51 -msgid "District" -msgstr "Gemarkung" - -#: intervention/filters.py:52 -msgid "Search for district" -msgstr "Nach Gemarkung suchen" - #: intervention/forms/forms.py:44 msgid "Construction XY; Location ABC" msgstr "Bauvorhaben XY; Flur ABC" @@ -1382,11 +1369,11 @@ msgstr "" msgid "Revocation" msgstr "Widerspruch" -#: intervention/tables.py:176 +#: intervention/tables.py:177 msgid "No revocation" msgstr "Kein Widerspruch" -#: intervention/tables.py:182 +#: intervention/tables.py:183 msgid "Revocation from {}, added on {} by {}" msgstr "Widerspruch vom {}, am {} von {} hinzugefügt" @@ -1521,6 +1508,62 @@ msgstr "" "somit nichts eingeben, bearbeiten oder sonstige Aktionen ausführen. " "Kontaktieren Sie bitte einen Administrator. +++" +#: konova/filters.py:67 +msgid "File number" +msgstr "Aktenzeichen" + +#: konova/filters.py:68 +msgid "Search for file number" +msgstr "Nach Aktenzeichen suchen" + +#: konova/filters.py:95 +msgid "District" +msgstr "Kreis" + +#: konova/filters.py:96 +msgid "Search for district" +msgstr "Nach Kreis suchen" + +#: konova/filters.py:108 +msgid "Parcel gmrkng" +msgstr "Gemarkung" + +#: konova/filters.py:109 +msgid "Search for parcel gmrkng" +msgstr "Nach Gemarkung suchen" + +#: konova/filters.py:121 konova/templates/konova/includes/parcels.html:18 +msgid "Parcel" +msgstr "Flur" + +#: konova/filters.py:122 +msgid "Search for parcel" +msgstr "Nach Flur suchen" + +#: konova/filters.py:134 konova/templates/konova/includes/parcels.html:19 +msgid "Parcel counter" +msgstr "Flurstückzähler" + +#: konova/filters.py:135 +msgid "Search for parcel counter" +msgstr "Nach Flurstückzähler suchen" + +#: konova/filters.py:148 konova/templates/konova/includes/parcels.html:20 +msgid "Parcel number" +msgstr "Flurstücknenner" + +#: konova/filters.py:149 +msgid "Search for parcel number" +msgstr "Nach Flurstücknenner suchen" + +#: konova/filters.py:279 +msgid "Show unshared" +msgstr "Nicht freigegebene anzeigen" + +#: konova/filters.py:323 +msgid "Show recorded" +msgstr "Verzeichnete anzeigen" + #: konova/forms.py:37 templates/form/collapsable/form.html:62 msgid "Save" msgstr "Speichern" @@ -1687,18 +1730,6 @@ msgstr "Kreis" msgid "Gemarkung" msgstr "Gemarkung" -#: konova/templates/konova/includes/parcels.html:18 -msgid "Parcel" -msgstr "Flur" - -#: konova/templates/konova/includes/parcels.html:19 -msgid "Parcel counter" -msgstr "Flurstückzähler" - -#: konova/templates/konova/includes/parcels.html:20 -msgid "Parcel number" -msgstr "Flurstücknenner" - #: konova/templates/konova/widgets/generate-content-input.html:6 msgid "Generate new" msgstr "Neu generieren" @@ -1899,11 +1930,11 @@ msgstr "Starte Suche" msgid "Results per page" msgstr "Treffer pro Seite" -#: templates/generic_index.html:82 templates/generic_index.html:88 +#: templates/generic_index.html:82 templates/generic_index.html:99 msgid "Filter" msgstr "" -#: templates/generic_index.html:90 +#: templates/generic_index.html:101 msgid "Apply filter" msgstr "Filter anwenden" diff --git a/templates/filter/checkbox_filter.html b/templates/filter/checkbox_filter.html new file mode 100644 index 00000000..93410368 --- /dev/null +++ b/templates/filter/checkbox_filter.html @@ -0,0 +1,5 @@ +{% for field in form %} +
+ {{field}} +
+{% endfor %} \ No newline at end of file diff --git a/templates/filter/query_filter.html b/templates/filter/query_filter.html new file mode 100644 index 00000000..24c867a9 --- /dev/null +++ b/templates/filter/query_filter.html @@ -0,0 +1,10 @@ +
+ {% for field in form %} + {% if not field.is_hidden %} +
+ {{field}} +
+ {% else %} + {% endif %} + {% endfor %} +
\ No newline at end of file diff --git a/templates/generic_index.html b/templates/generic_index.html index cf3253a3..729be8e4 100644 --- a/templates/generic_index.html +++ b/templates/generic_index.html @@ -3,6 +3,16 @@ {% load django_tables2 %} {% load i18n static fontawesome_5 %} +{% 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 %} + {% block body %}
{% if table.title %} @@ -41,7 +51,7 @@
- +
- {{ table.filter.form.as_p }} - +
+
+ {% with table.filter.selection_filter.form as form %} + {% include 'filter/query_filter.html' %} + {% endwith %} +
+
+
+ {% with table.filter.query_filter.form as form %} + {% include 'filter/query_filter.html' %} + {% endwith %} +
+
+
+ {% with table.filter.checkbox_filter.form as form %} + {% include 'filter/checkbox_filter.html' %} + {% endwith %} +
+
+
+ +
+