Table filters

* adds table filtering for InterventionTable
* adds default ordering
* moves user-access filtering from view to InterventionTableFiler
* adds generic render_icn method for BaseTable (for usage with fontawesome5)
* adds translations
* improves table.html template
This commit is contained in:
mipel 2021-07-22 10:00:59 +02:00
parent f88b206756
commit 0c1fa72556
8 changed files with 228 additions and 29 deletions

113
intervention/filters.py Normal file
View File

@ -0,0 +1,113 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
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.models import Intervention
class InterventionTableFilter(django_filters.FilterSet):
q = django_filters.Filter(
method='_filter_by_keyword',
widget=forms.HiddenInput(), # use search bar in template, we only need the filter logic in here!
)
sa = django_filters.BooleanFilter(
method='_filter_show_all',
label=_("Show all"),
label_suffix=_(""),
widget=forms.CheckboxInput()
)
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)
def _filter_by_keyword(self, queryset, name, value) -> QuerySet:
""" Filters queryset depending on value of search bar input
Args:
queryset ():
name ():
value ():
Returns:
"""
# 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_on=None,
)
else:
return queryset

View File

@ -5,11 +5,13 @@ Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 01.12.20 Created on: 01.12.20
""" """
from django.http import HttpRequest
from django.urls import reverse from django.urls import reverse
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.timezone import localtime from django.utils.timezone import localtime
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
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 from konova.sub_settings.django_settings import DEFAULT_DATE_TIME_FORMAT
from konova.utils.tables import BaseTable from konova.utils.tables import BaseTable
@ -39,6 +41,12 @@ class InterventionTable(BaseTable):
empty_values=[], empty_values=[],
accessor="recorded_on", accessor="recorded_on",
) )
e = tables.Column(
verbose_name=_("Editable"),
orderable=True,
empty_values=[],
accessor="users",
)
lm = tables.Column( lm = tables.Column(
verbose_name=_("Last edit"), verbose_name=_("Last edit"),
orderable=True, orderable=True,
@ -58,10 +66,16 @@ class InterventionTable(BaseTable):
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
template_name = "django_tables2/bootstrap4.html" template_name = "django_tables2/bootstrap4.html"
def __init__(self, *args, **kwargs): def __init__(self, request: HttpRequest, *args, **kwargs):
super().__init__(*args, **kwargs)
self.title = _("Interventions") self.title = _("Interventions")
self.add_new_url = reverse("intervention:new") self.add_new_url = reverse("intervention:new")
qs = kwargs.get("queryset", None)
self.filter = InterventionTableFilter(
user=request.user,
data=request.GET,
queryset=qs,
)
super().__init__(request, self.filter, *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
@ -128,6 +142,27 @@ class InterventionTable(BaseTable):
) )
return format_html(html) return format_html(html)
def render_e(self, value, record: Intervention):
""" Renders the registered column for an intervention
Args:
value (str): The identifier value
record (Intervention): The intervention record
Returns:
"""
html = ""
has_access = value.filter(
username=self.user.username
).exists()
html += self.render_icn(
tooltip=_("Full access granted") if has_access else _("Access not granted"),
icn_class="fas fa-edit rlp-r-inv" if has_access else "far fa-edit",
)
return format_html(html)
def render_ac(self, value, record): def render_ac(self, value, record):
""" """
Renders possible actions for this record, such as delete. Renders possible actions for this record, such as delete.

View File

@ -24,11 +24,13 @@ def index_view(request: HttpRequest):
A rendered view A rendered view
""" """
template = "generic_index.html" template = "generic_index.html"
user = request.user
# Filtering by user access is performed in table filter inside of InterventionTableFilter class
interventions = Intervention.objects.filter( interventions = Intervention.objects.filter(
deleted_on=None, # not deleted deleted_on=None, # not deleted
next_version=None, # only newest versions next_version=None, # only newest versions
users__in=[user], # requesting user has access ).order_by(
"-created_on"
) )
table = InterventionTable( table = InterventionTable(
request=request, request=request,

View File

@ -37,6 +37,10 @@ Declare some basic colours
color:var(--rlp-red); color:var(--rlp-red);
} }
.rlp-r-inv{
color:var(--rlp-red);
}
html { html {
position: relative; position: relative;
min-height: 100%; min-height: 100%;

View File

@ -126,6 +126,16 @@ class BaseTable(tables.tables.Table):
icon icon
) )
def render_icn(self, tooltip: str = None, icn_class: str = None):
"""
Returns a rendered fontawesome icon
"""
return format_html(
"<em title='{}' class='{}'></em>",
tooltip,
icn_class,
)
class ChoicesColumnForm(BaseForm): class ChoicesColumnForm(BaseForm):
select = forms.ChoiceField( select = forms.ChoiceField(

Binary file not shown.

View File

@ -3,13 +3,15 @@
# 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.
# #
#: konova/forms.py:69 user/forms.py:38 #: intervention/filters.py:21 intervention/filters.py:27
#: intervention/filters.py:34 intervention/filters.py:35 konova/forms.py:69
#: user/forms.py:38
#, fuzzy #, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-07-21 15:07+0200\n" "POT-Creation-Date: 2021-07-22 09:31+0200\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"
@ -20,12 +22,12 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: compensation/tables.py:18 compensation/tables.py:71 intervention/forms.py:26 #: compensation/tables.py:18 compensation/tables.py:71 intervention/forms.py:26
#: intervention/tables.py:22 #: intervention/tables.py:23
msgid "Identifier" msgid "Identifier"
msgstr "Kennung" msgstr "Kennung"
#: compensation/tables.py:23 compensation/tables.py:76 intervention/forms.py:33 #: compensation/tables.py:23 compensation/tables.py:76 intervention/forms.py:33
#: intervention/tables.py:27 #: intervention/tables.py:28
msgid "Title" msgid "Title"
msgstr "Titel" msgstr "Titel"
@ -47,17 +49,17 @@ msgid "Compensation"
msgstr "Kompensation" msgstr "Kompensation"
#: compensation/tables.py:54 compensation/tables.py:107 #: compensation/tables.py:54 compensation/tables.py:107
#: intervention/tables.py:79 intervention/tables.py:139 #: intervention/tables.py:92 intervention/tables.py:179
msgid "Open {}" msgid "Open {}"
msgstr "Öffne {}" msgstr "Öffne {}"
#: compensation/tables.py:59 compensation/tables.py:112 #: compensation/tables.py:59 compensation/tables.py:112
#: intervention/tables.py:143 #: intervention/tables.py:183
msgid "Edit {}" msgid "Edit {}"
msgstr "Bearbeite {}" msgstr "Bearbeite {}"
#: compensation/tables.py:63 compensation/tables.py:116 #: compensation/tables.py:63 compensation/tables.py:116
#: intervention/tables.py:147 #: intervention/tables.py:187
msgid "Delete {}" msgid "Delete {}"
msgstr "Lösche {}" msgstr "Lösche {}"
@ -65,6 +67,22 @@ msgstr "Lösche {}"
msgid "Eco Accounts" msgid "Eco Accounts"
msgstr "Ökokonten" msgstr "Ökokonten"
#: intervention/filters.py:20
msgid "Show all"
msgstr "Alle anzeigen"
#: intervention/filters.py:26
msgid "Show recorded"
msgstr "Verzeichnete anzeigen"
#: intervention/filters.py:38
msgid "District"
msgstr "Gemarkung"
#: intervention/filters.py:39
msgid "Search for district"
msgstr "Nach Gemarkung suchen"
#: intervention/forms.py:29 #: intervention/forms.py:29
msgid "Generated automatically if none was given" msgid "Generated automatically if none was given"
msgstr "Wird automatisch erzeugt, falls nicht angegeben" msgstr "Wird automatisch erzeugt, falls nicht angegeben"
@ -133,57 +151,69 @@ msgstr "Neuer Eingriff"
msgid "Edit intervention" msgid "Edit intervention"
msgstr "Eingriff bearbeiten" msgstr "Eingriff bearbeiten"
#: intervention/tables.py:32 #: intervention/tables.py:33
msgid "Checked" msgid "Checked"
msgstr "Geprüft" msgstr "Geprüft"
#: intervention/tables.py:38 #: intervention/tables.py:39
msgid "Registered" msgid "Registered"
msgstr "Verzeichnet" msgstr "Verzeichnet"
#: intervention/tables.py:44 #: intervention/tables.py:45
msgid "Editable"
msgstr "Freigegeben"
#: intervention/tables.py:51
msgid "Last edit" msgid "Last edit"
msgstr "Zuletzt bearbeitet" msgstr "Zuletzt bearbeitet"
#: intervention/tables.py:64 #: intervention/tables.py:70
msgid "Interventions" msgid "Interventions"
msgstr "Eingriffe" msgstr "Eingriffe"
#: intervention/tables.py:79 intervention/tables.py:136 #: intervention/tables.py:92 intervention/tables.py:176
#: intervention/templates/intervention/open.html:8 #: intervention/templates/intervention/open.html:8
#: konova/templates/konova/home.html:11 templates/navbar.html:22 #: konova/templates/konova/home.html:11 templates/navbar.html:22
msgid "Intervention" msgid "Intervention"
msgstr "Eingriff" msgstr "Eingriff"
#: intervention/tables.py:98 #: intervention/tables.py:111
msgid "Not checked yet" msgid "Not checked yet"
msgstr "Noch nicht geprüft" msgstr "Noch nicht geprüft"
#: intervention/tables.py:102 #: intervention/tables.py:115
msgid "Checked on {} by {}" msgid "Checked on {} by {}"
msgstr "Am {} von {} geprüft worden" msgstr "Am {} von {} geprüft worden"
#: intervention/tables.py:121 #: intervention/tables.py:134
msgid "Not registered yet" msgid "Not registered yet"
msgstr "Noch nicht verzeichnet" msgstr "Noch nicht verzeichnet"
#: intervention/tables.py:125 #: intervention/tables.py:138
msgid "Registered on {} by {}" msgid "Registered on {} by {}"
msgstr "Am {} von {} verzeichnet worden" msgstr "Am {} von {} verzeichnet worden"
#: intervention/tables.py:167
msgid "Full access granted"
msgstr "Für Sie freigegeben - Datensatz kann bearbeitet werden"
#: intervention/tables.py:167
msgid "Access not granted"
msgstr "Nicht freigegeben - Datensatz nur lesbar"
#: intervention/templates/intervention/open.html:12 #: intervention/templates/intervention/open.html:12
msgid "Edit" msgid "Edit"
msgstr "Bearbeiten" msgstr "Bearbeiten"
#: intervention/views.py:60 #: intervention/views.py:63
msgid "Intervention {} added" msgid "Intervention {} added"
msgstr "Eingriff {} hinzugefügt" msgstr "Eingriff {} hinzugefügt"
#: intervention/views.py:63 intervention/views.py:116 #: intervention/views.py:66 intervention/views.py:119
msgid "Invalid input" msgid "Invalid input"
msgstr "Eingabe fehlerhaft" msgstr "Eingabe fehlerhaft"
#: intervention/views.py:113 #: intervention/views.py:116
msgid "{} edited" msgid "{} edited"
msgstr "{} bearbeitet" msgstr "{} bearbeitet"
@ -371,10 +401,14 @@ msgstr "Starte Suche"
msgid "Results per page" msgid "Results per page"
msgstr "Treffer pro Seite" msgstr "Treffer pro Seite"
#: templates/table.html:70 #: templates/table.html:70 templates/table.html:77
msgid "Filter" msgid "Filter"
msgstr "" msgstr ""
#: templates/table.html:79
msgid "Apply filter"
msgstr "Filter anwenden"
#: user/forms.py:23 #: user/forms.py:23
msgid "Notifications" msgid "Notifications"
msgstr "Benachrichtigungen" msgstr "Benachrichtigungen"
@ -1697,9 +1731,6 @@ msgstr ""
#~ msgid "Process management" #~ msgid "Process management"
#~ msgstr "Vorgangsverwaltung" #~ msgstr "Vorgangsverwaltung"
#~ msgid "Show process"
#~ msgstr "Zeige Vorgänge"
#~ msgid "New process" #~ msgid "New process"
#~ msgstr "Neuer Vorgang" #~ msgstr "Neuer Vorgang"

View File

@ -26,9 +26,9 @@
{% endcomment %} {% endcomment %}
<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">
<form method="get" action="{{table.filter}}"> <form method="get">
<div class="input-group"> <div class="input-group">
<input type="text" class="form-control" aria-label="{% trans 'Search for keywords' %}" placeholder="{% trans 'Search' %}"> <input id="id_q" name="q" 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="">
@ -72,6 +72,10 @@
<div class="card-body"> <div class="card-body">
<form method="get"> <form method="get">
{{ table.filter.form.as_p }} {{ table.filter.form.as_p }}
<button class="btn btn-default" title="{% trans 'Filter' %}">
{% fa5_icon 'filter' %}
{% trans 'Apply filter' %}
</button>
</form> </form>
</div> </div>
</div> </div>