Merge pull request '61_Extend_filter' (#64) from 61_Extend_filter into master

Reviewed-on: SGD-Nord/konova#64
This commit is contained in:
Michel Peltriaux 2022-01-12 10:20:19 +01:00
commit 31b3428146
17 changed files with 780 additions and 235 deletions

View File

@ -8,20 +8,45 @@ Created on: 29.07.21
import django_filters import django_filters
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django import forms 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): class SelectionCompensationTableFilter(SelectionTableFilter):
""" TableFilter for compensations """ 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 """ Filters queryset depending on value of 'show_all' setting
Args: Args:
@ -39,7 +64,7 @@ class CompensationTableFilter(InterventionTableFilter):
else: else:
return queryset 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 """ Filters queryset depending on value of 'show_recorded' setting
Args: Args:
@ -58,21 +83,51 @@ class CompensationTableFilter(InterventionTableFilter):
return queryset return queryset
class EcoAccountTableFilter(InterventionTableFilter): class CompensationTableFilter(AbstractTableFilter):
""" TableFilter for eco accounts """ TableFilter for compensations
Based widely on InterventionTableFilter. 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( sr = django_filters.BooleanFilter(
method='_filter_only_show_unrecorded', method='filter_only_show_unrecorded',
label=_("Show only unrecorded"), label=_("Show only unrecorded"),
label_suffix=_(""), 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 """ Filters queryset depending on value of 'show_all' setting
Args: Args:
@ -90,7 +145,7 @@ class EcoAccountTableFilter(InterventionTableFilter):
else: else:
return queryset 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 """ Filters queryset depending on value of 'show_recorded' setting
Args: Args:
@ -107,3 +162,40 @@ class EcoAccountTableFilter(InterventionTableFilter):
) )
else: else:
return queryset 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

View File

@ -16,11 +16,11 @@ from django.utils.translation import gettext_lazy as _
from compensation.filters import CompensationTableFilter, EcoAccountTableFilter from compensation.filters import CompensationTableFilter, EcoAccountTableFilter
from compensation.models import Compensation, EcoAccount from compensation.models import Compensation, EcoAccount
from konova.sub_settings.django_settings import DEFAULT_DATE_TIME_FORMAT from konova.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 import django_tables2 as tables
class CompensationTable(BaseTable): class CompensationTable(BaseTable, TableRenderMixin):
id = tables.Column( id = tables.Column(
verbose_name=_("Identifier"), verbose_name=_("Identifier"),
orderable=True, orderable=True,
@ -67,7 +67,8 @@ class CompensationTable(BaseTable):
data=request.GET, data=request.GET,
queryset=qs, 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): def render_id(self, value, record: Compensation):
""" Renders the id column for a compensation """ Renders the id column for a compensation
@ -161,7 +162,7 @@ class CompensationTable(BaseTable):
return format_html(html) return format_html(html)
class EcoAccountTable(BaseTable): class EcoAccountTable(BaseTable, TableRenderMixin):
id = tables.Column( id = tables.Column(
verbose_name=_("Identifier"), verbose_name=_("Identifier"),
orderable=True, orderable=True,
@ -207,7 +208,8 @@ class EcoAccountTable(BaseTable):
data=request.GET, data=request.GET,
queryset=qs, 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): def render_id(self, value, record: EcoAccount):
""" Renders the id column for an eco account """ Renders the id column for an eco account

View File

@ -5,8 +5,6 @@ Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 19.08.21 Created on: 19.08.21
""" """
from django.db.models import QuerySet
from compensation.filters import EcoAccountTableFilter from compensation.filters import EcoAccountTableFilter

View File

@ -14,12 +14,12 @@ from django.urls import reverse
import django_tables2 as tables import django_tables2 as tables
from konova.sub_settings.django_settings import DEFAULT_DATE_TIME_FORMAT 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.filters import EmaTableFilter
from ema.models import Ema 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 Since EMA and compensation are basically the same, we can reuse CompensationTableFilter and extend the EMA filter
in the future by inheriting. in the future by inheriting.
@ -65,7 +65,8 @@ class EmaTable(BaseTable):
data=request.GET, data=request.GET,
queryset=qs, 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): def render_id(self, value, record: Ema):
""" Renders the id column for a EMA """ Renders the id column for a EMA

View File

@ -5,119 +5,29 @@ Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 22.07.21 Created on: 22.07.21
""" """
import django_filters from konova.filters.table_filters import AbstractTableFilter, SelectionTableFilter, QueryTableFilter, CheckboxTableFilter
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
class InterventionTableFilter(django_filters.FilterSet): class InterventionTableFilter(AbstractTableFilter):
""" TableFilter for Intervention model def __init__(self, user=None, *args, **kwargs):
"""
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
super().__init__(*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: # Pipe the queryset through all needed filters
""" Filters queryset depending on value of search bar input 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

View File

@ -14,11 +14,11 @@ from django.utils.translation import gettext_lazy as _
from intervention.filters import InterventionTableFilter from intervention.filters import InterventionTableFilter
from intervention.models import Intervention from intervention.models import Intervention
from konova.sub_settings.django_settings import DEFAULT_DATE_TIME_FORMAT, DEFAULT_DATE_FORMAT 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 import django_tables2 as tables
class InterventionTable(BaseTable): class InterventionTable(BaseTable, TableRenderMixin):
id = tables.Column( id = tables.Column(
verbose_name=_("Identifier"), verbose_name=_("Identifier"),
orderable=True, orderable=True,
@ -71,7 +71,8 @@ class InterventionTable(BaseTable):
data=request.GET, data=request.GET,
queryset=qs, 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): def render_id(self, value, record: Intervention):
""" Renders the id column for an intervention """ Renders the id column for an intervention

View File

@ -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
"""

406
konova/filters/mixins.py Normal file
View File

@ -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

View File

@ -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

View File

@ -110,9 +110,13 @@ class Geometry(BaseResource):
underlying_parcels = [] underlying_parcels = []
for result in fetched_parcels: for result in fetched_parcels:
fetched_parcel = result[typename] 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( parcel_obj = Parcel.objects.get_or_create(
gmrkng=fetched_parcel["ave:gemarkung"], gmrkng=fetched_parcel["ave:gemarkung"],
flr=fetched_parcel["ave:flur"], flr=flr_val,
flrstck_nnr=fetched_parcel['ave:flstnrnen'], flrstck_nnr=fetched_parcel['ave:flstnrnen'],
flrstck_zhlr=fetched_parcel['ave:flstnrzae'], flrstck_zhlr=fetched_parcel['ave:flstnrzae'],
)[0] )[0]

View File

@ -404,6 +404,7 @@ class AutocompleteTestCase(BaseViewTestCase):
cls.atcmplt_code_comp_process = reverse("codes-process-type-autocomplete") 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_reg_off = reverse("codes-registration-office-autocomplete")
cls.atcmplt_code_comp_cons_off = reverse("codes-conservation-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): def _test_views_anonymous_user(self):
# ATTENTION: As of the current state of django-autocomplete-light, there is no way to check on authenticated # 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_process,
self.atcmplt_code_comp_reg_off, self.atcmplt_code_comp_reg_off,
self.atcmplt_code_comp_cons_off, self.atcmplt_code_comp_cons_off,
self.atcmplt_code_share_user,
] ]
self.assert_url_fail(client, urls) self.assert_url_fail(client, urls)

View File

@ -5,15 +5,13 @@ Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 25.11.20 Created on: 25.11.20
""" """
import uuid
from django import forms
from django.core.paginator import PageNotAnInteger, EmptyPage from django.core.paginator import PageNotAnInteger, EmptyPage
from django.http import HttpRequest from django.http import HttpRequest
from django.utils.html import format_html from django.utils.html import format_html
import django_tables2 as tables 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 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", "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 self.user = request.user or None
if filter_set is not None:
queryset = filter_set.qs
kwargs["data"] = queryset kwargs["data"] = queryset
kwargs["request"] = request kwargs["request"] = request
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@ -149,22 +145,21 @@ class BaseTable(tables.tables.Table):
) )
class ChoicesColumnForm(BaseForm): class TableRenderMixin:
select = forms.ChoiceField( """ Holds different render methods for general purposes
choices=[],
label="",
label_suffix="",
widget=forms.Select(
attrs={
"onchange": "submit();",
}
)
)
def __init__(self, *args, **kwargs): """
self.action_url = kwargs.pop("action_url", None) def render_t(self, value, record: BaseObject):
self.choices = kwargs.pop("choices", []) """ Renders a BaseObject title
super().__init__(*args, **kwargs)
self.auto_id += "_" + str(uuid.uuid4()) Args:
if len(self.choices) > 0: value ():
self.fields["select"].choices = self.choices record ():
Returns:
"""
max_length = 75
if len(value) > max_length:
value = f"{value[:max_length]}..."
return value

Binary file not shown.

View File

@ -3,23 +3,26 @@
# This file is distributed under the same license as the PACKAGE package. # This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # FIRST AUTHOR <EMAIL@ADDRESS>, 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:45 compensation/forms/modalForms.py:61
#: compensation/forms/modalForms.py:238 compensation/forms/modalForms.py:316 #: 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: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:140 konova/forms.py:241 konova/forms.py:312 #: konova/filters.py:63 konova/filters.py:64 konova/filters.py:91
#: konova/forms.py:339 konova/forms.py:349 konova/forms.py:362 #: konova/filters.py:92 konova/filters.py:104 konova/filters.py:105
#: konova/forms.py:374 konova/forms.py:392 user/forms.py:38 #: 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 #, 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: 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" "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"
@ -145,7 +148,7 @@ msgstr "Geprüft"
#: analysis/templates/analysis/reports/includes/intervention/compensated_by.html:9 #: analysis/templates/analysis/reports/includes/intervention/compensated_by.html:9
#: analysis/templates/analysis/reports/includes/intervention/laws.html:20 #: analysis/templates/analysis/reports/includes/intervention/laws.html:20
#: analysis/templates/analysis/reports/includes/old_data/amount.html:18 #: 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/compensation/view.html:77
#: compensation/templates/compensation/detail/eco_account/includes/deductions.html:31 #: compensation/templates/compensation/detail/eco_account/includes/deductions.html:31
#: compensation/templates/compensation/detail/eco_account/view.html:44 #: 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/intervention/compensated_by.html:15
#: analysis/templates/analysis/reports/includes/old_data/amount.html:29 #: 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 #: compensation/templates/compensation/detail/compensation/view.html:19
#: konova/templates/konova/home.html:49 templates/navbars/navbar.html:28 #: konova/templates/konova/home.html:49 templates/navbars/navbar.html:28
msgid "Compensation" msgid "Compensation"
@ -276,14 +279,14 @@ msgstr "Typ"
#: analysis/templates/analysis/reports/includes/old_data/amount.html:24 #: analysis/templates/analysis/reports/includes/old_data/amount.html:24
#: intervention/forms/modalForms.py:306 intervention/forms/modalForms.py:313 #: 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 #: intervention/templates/intervention/detail/view.html:19
#: konova/templates/konova/home.html:11 templates/navbars/navbar.html:22 #: konova/templates/konova/home.html:11 templates/navbars/navbar.html:22
msgid "Intervention" msgid "Intervention"
msgstr "Eingriff" msgstr "Eingriff"
#: analysis/templates/analysis/reports/includes/old_data/amount.html:34 #: 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 #: compensation/templates/compensation/detail/eco_account/view.html:19
#: intervention/forms/modalForms.py:279 intervention/forms/modalForms.py:286 #: intervention/forms/modalForms.py:279 intervention/forms/modalForms.py:286
#: konova/templates/konova/home.html:88 templates/navbars/navbar.html:34 #: konova/templates/konova/home.html:88 templates/navbars/navbar.html:34
@ -298,12 +301,12 @@ msgstr "Altfälle"
msgid "Before" msgid "Before"
msgstr "Vor" msgstr "Vor"
#: compensation/filters.py:70 #: compensation/filters.py:90
msgid "Show only unrecorded" msgid "Show only unrecorded"
msgstr "Nur unverzeichnete anzeigen" msgstr "Nur unverzeichnete anzeigen"
#: compensation/forms/forms.py:32 compensation/tables.py:25 #: 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/tables.py:23
#: intervention/templates/intervention/detail/includes/compensations.html:30 #: intervention/templates/intervention/detail/includes/compensations.html:30
msgid "Identifier" msgid "Identifier"
@ -314,7 +317,7 @@ msgid "Generated automatically"
msgstr "Automatisch generiert" msgstr "Automatisch generiert"
#: compensation/forms/forms.py:44 compensation/tables.py:30 #: 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/includes/documents.html:28
#: compensation/templates/compensation/detail/compensation/view.html:31 #: compensation/templates/compensation/detail/compensation/view.html:31
#: compensation/templates/compensation/detail/eco_account/includes/documents.html:28 #: 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 " "Es wurde bereits mehr Fläche abgebucht, als Sie nun als abbuchbar einstellen "
"wollen. Kontaktieren Sie die für die Abbuchungen verantwortlichen Nutzer!" "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 #: intervention/tables.py:51
msgid "Editable" msgid "Editable"
msgstr "Freigegeben" 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 #: intervention/tables.py:57
msgid "Last edit" msgid "Last edit"
msgstr "Zuletzt bearbeitet" msgstr "Zuletzt bearbeitet"
#: compensation/tables.py:84 compensation/tables.py:224 ema/tables.py:82 #: compensation/tables.py:85 compensation/tables.py:226 ema/tables.py:83
#: intervention/tables.py:88 #: intervention/tables.py:89
msgid "Open {}" msgid "Open {}"
msgstr "Öffne {}" msgstr "Öffne {}"
#: compensation/tables.py:105 intervention/tables.py:107 #: compensation/tables.py:106 intervention/tables.py:108
msgid "Not checked yet" msgid "Not checked yet"
msgstr "Noch nicht geprüft" 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 {}" msgid "Checked on {} by {}"
msgstr "Am {} von {} geprüft worden" 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/compensation/view.html:80
#: compensation/templates/compensation/detail/eco_account/includes/deductions.html:56 #: compensation/templates/compensation/detail/eco_account/includes/deductions.html:56
#: compensation/templates/compensation/detail/eco_account/view.html:47 #: compensation/templates/compensation/detail/eco_account/view.html:47
#: ema/tables.py:101 ema/templates/ema/detail/view.html:38 #: ema/tables.py:102 ema/templates/ema/detail/view.html:38
#: intervention/tables.py:131 #: intervention/tables.py:132
#: intervention/templates/intervention/detail/view.html:85 #: intervention/templates/intervention/detail/view.html:85
msgid "Not recorded yet" msgid "Not recorded yet"
msgstr "Noch nicht verzeichnet" msgstr "Noch nicht verzeichnet"
#: compensation/tables.py:134 compensation/tables.py:262 ema/tables.py:106 #: compensation/tables.py:135 compensation/tables.py:264 ema/tables.py:107
#: intervention/tables.py:136 #: intervention/tables.py:137
msgid "Recorded on {} by {}" msgid "Recorded on {} by {}"
msgstr "Am {} von {} verzeichnet worden" msgstr "Am {} von {} verzeichnet worden"
#: compensation/tables.py:158 compensation/tables.py:284 ema/tables.py:129 #: compensation/tables.py:159 compensation/tables.py:286 ema/tables.py:130
#: intervention/tables.py:159 #: intervention/tables.py:160
msgid "Full access granted" msgid "Full access granted"
msgstr "Für Sie freigegeben - Datensatz kann bearbeitet werden" msgstr "Für Sie freigegeben - Datensatz kann bearbeitet werden"
#: compensation/tables.py:158 compensation/tables.py:284 ema/tables.py:129 #: compensation/tables.py:159 compensation/tables.py:286 ema/tables.py:130
#: intervention/tables.py:159 #: intervention/tables.py:160
msgid "Access not granted" msgid "Access not granted"
msgstr "Nicht freigegeben - Datensatz nur lesbar" msgstr "Nicht freigegeben - Datensatz nur lesbar"
#: compensation/tables.py:176 #: compensation/tables.py:177
#: compensation/templates/compensation/detail/eco_account/view.html:35 #: compensation/templates/compensation/detail/eco_account/view.html:35
#: konova/templates/konova/widgets/progressbar.html:3 #: konova/templates/konova/widgets/progressbar.html:3
msgid "Available" msgid "Available"
msgstr "Verfügbar" msgstr "Verfügbar"
#: compensation/tables.py:202 #: compensation/tables.py:203
msgid "Eco Accounts" msgid "Eco Accounts"
msgstr "Ökokonten" msgstr "Ökokonten"
#: compensation/tables.py:257 #: compensation/tables.py:259
msgid "Not recorded yet. Can not be used for deductions, yet." msgid "Not recorded yet. Can not be used for deductions, yet."
msgstr "" msgstr ""
"Noch nicht verzeichnet. Kann noch nicht für Abbuchungen genutzt werden." "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 " "Maßnahmen aus Ersatzzahlungen, die nach 2015 rechtskräftig wurden, werden "
"durch die Stiftung Natur und Umwelt verwaltet." "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" msgid "EMA"
msgstr "" msgstr ""
@ -1197,22 +1200,6 @@ msgstr "EMA {} bearbeitet"
msgid "EMA removed" msgid "EMA removed"
msgstr "EMA entfernt" 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 #: intervention/forms/forms.py:44
msgid "Construction XY; Location ABC" msgid "Construction XY; Location ABC"
msgstr "Bauvorhaben XY; Flur ABC" msgstr "Bauvorhaben XY; Flur ABC"
@ -1382,11 +1369,11 @@ msgstr ""
msgid "Revocation" msgid "Revocation"
msgstr "Widerspruch" msgstr "Widerspruch"
#: intervention/tables.py:176 #: intervention/tables.py:177
msgid "No revocation" msgid "No revocation"
msgstr "Kein Widerspruch" msgstr "Kein Widerspruch"
#: intervention/tables.py:182 #: intervention/tables.py:183
msgid "Revocation from {}, added on {} by {}" msgid "Revocation from {}, added on {} by {}"
msgstr "Widerspruch vom {}, am {} von {} hinzugefügt" msgstr "Widerspruch vom {}, am {} von {} hinzugefügt"
@ -1521,6 +1508,62 @@ msgstr ""
"somit nichts eingeben, bearbeiten oder sonstige Aktionen ausführen. " "somit nichts eingeben, bearbeiten oder sonstige Aktionen ausführen. "
"Kontaktieren Sie bitte einen Administrator. +++" "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 #: konova/forms.py:37 templates/form/collapsable/form.html:62
msgid "Save" msgid "Save"
msgstr "Speichern" msgstr "Speichern"
@ -1687,18 +1730,6 @@ msgstr "Kreis"
msgid "Gemarkung" msgid "Gemarkung"
msgstr "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 #: konova/templates/konova/widgets/generate-content-input.html:6
msgid "Generate new" msgid "Generate new"
msgstr "Neu generieren" msgstr "Neu generieren"
@ -1899,11 +1930,11 @@ msgstr "Starte Suche"
msgid "Results per page" msgid "Results per page"
msgstr "Treffer pro Seite" 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" msgid "Filter"
msgstr "" msgstr ""
#: templates/generic_index.html:90 #: templates/generic_index.html:101
msgid "Apply filter" msgid "Apply filter"
msgstr "Filter anwenden" msgstr "Filter anwenden"

View File

@ -0,0 +1,5 @@
{% for field in form %}
<div class="form-check form-check-inline">
{{field}} <label class="form-check-label" for="id_{{field.name}}">{{field.label}}</label>
</div>
{% endfor %}

View File

@ -0,0 +1,10 @@
<div class="form-row align-items-center">
{% for field in form %}
{% if not field.is_hidden %}
<div class="col-auto">
{{field}}
</div>
{% else %}
{% endif %}
{% endfor %}
</div>

View File

@ -3,6 +3,16 @@
{% load django_tables2 %} {% load django_tables2 %}
{% load i18n static fontawesome_5 %} {% 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 %} {% block body %}
<div class="col-md"> <div class="col-md">
{% if table.title %} {% if table.title %}
@ -41,7 +51,7 @@
<div class="row my-1"> <div class="row my-1">
<div class="col-sm-12 col-md-8 col-lg-6"> <div class="col-sm-12 col-md-8 col-lg-6">
<div class="input-group"> <div class="input-group">
<input id="id_{{table.filter.filters.q.field_name}}" name="{{table.filter.filters.q.field_name}}" type="text" class="form-control" aria-label="{% trans 'Search for keywords' %}" placeholder="{% trans 'Search' %}" value="{{ request.GET.q }}"> <input id="id_{{table.filter.query_filter.filters.q.field_name}}" name="{{table.filter.query_filter.filters.q.field_name}}" type="text" class="form-control" aria-label="{% trans 'Search for keywords' %}" placeholder="{% trans 'Search' %}" value="{{ request.GET.q }}">
<div class="input-group-append" title="{% trans 'Start search' %}"> <div class="input-group-append" title="{% trans 'Start search' %}">
<button type="submit" class="btn btn-default input-group-text"> <button type="submit" class="btn btn-default input-group-text">
<span class=""> <span class="">
@ -82,11 +92,32 @@
</div> </div>
<div id="filter" class="collapse" aria-labelledby="filterHeader"> <div id="filter" class="collapse" aria-labelledby="filterHeader">
<div class="card-body"> <div class="card-body">
{{ table.filter.form.as_p }} <div class="">
<button class="btn btn-default" title="{% trans 'Filter' %}"> <div class="">
{% fa5_icon 'filter' %} {% with table.filter.selection_filter.form as form %}
{% trans 'Apply filter' %} {% include 'filter/query_filter.html' %}
</button> {% endwith %}
</div>
<hr>
<div class="mt-3">
{% with table.filter.query_filter.form as form %}
{% include 'filter/query_filter.html' %}
{% endwith %}
</div>
<hr>
<div class="mt-3">
{% with table.filter.checkbox_filter.form as form %}
{% include 'filter/checkbox_filter.html' %}
{% endwith %}
</div>
<hr>
<div class="mt-3">
<button class="btn btn-default" title="{% trans 'Filter' %}">
{% fa5_icon 'filter' %}
{% trans 'Apply filter' %}
</button>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>