Compare commits
No commits in common. "75777d65c6d30777d4340f01f35ab5c22b36eaf4" and "de8d79983d6e9962797fe6b211e3a066206b289c" have entirely different histories.
75777d65c6
...
de8d79983d
@ -55,7 +55,7 @@ class TimespanReportForm(BaseForm):
|
|||||||
code_lists__in=[CODELIST_CONSERVATION_OFFICE_ID],
|
code_lists__in=[CODELIST_CONSERVATION_OFFICE_ID],
|
||||||
),
|
),
|
||||||
widget=autocomplete.ModelSelect2(
|
widget=autocomplete.ModelSelect2(
|
||||||
url="codelist:conservation-office-autocomplete",
|
url="codes-conservation-office-autocomplete",
|
||||||
attrs={
|
attrs={
|
||||||
"data-placeholder": _("Click for selection")
|
"data-placeholder": _("Click for selection")
|
||||||
}
|
}
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
||||||
Created on: 18.08.22
|
|
||||||
|
|
||||||
"""
|
|
@ -1,74 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
||||||
Created on: 18.08.22
|
|
||||||
|
|
||||||
"""
|
|
||||||
from dal_select2.views import Select2GroupQuerySetView
|
|
||||||
from django.db.models import Q
|
|
||||||
|
|
||||||
from codelist.models import KonovaCode
|
|
||||||
|
|
||||||
|
|
||||||
class KonovaCodeAutocomplete(Select2GroupQuerySetView):
|
|
||||||
"""
|
|
||||||
Provides simple autocomplete functionality for codes
|
|
||||||
|
|
||||||
Parameter support:
|
|
||||||
* q: Search for a word inside long_name of a code
|
|
||||||
* c: Search inside a special codelist
|
|
||||||
|
|
||||||
"""
|
|
||||||
paginate_by = 50
|
|
||||||
|
|
||||||
def order_by(self, qs):
|
|
||||||
""" Orders by a predefined value
|
|
||||||
|
|
||||||
Wrapped in a function to provide inheritance-based different orders
|
|
||||||
|
|
||||||
Args:
|
|
||||||
qs (QuerySet): The queryset to be ordered
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
qs (QuerySet): The ordered queryset
|
|
||||||
"""
|
|
||||||
return qs.order_by(
|
|
||||||
"long_name"
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
if self.request.user.is_anonymous:
|
|
||||||
return KonovaCode.objects.none()
|
|
||||||
qs = KonovaCode.objects.filter(
|
|
||||||
is_archived=False,
|
|
||||||
is_selectable=True,
|
|
||||||
is_leaf=True,
|
|
||||||
)
|
|
||||||
qs = self.order_by(qs)
|
|
||||||
if self.c:
|
|
||||||
qs = qs.filter(
|
|
||||||
code_lists__in=[self.c]
|
|
||||||
)
|
|
||||||
if self.q:
|
|
||||||
# Remove whitespaces from self.q and split input in all keywords (if multiple given)
|
|
||||||
q = dict.fromkeys(self.q.strip().split(" "))
|
|
||||||
# Create one filter looking up for all keys where all keywords can be found in the same result
|
|
||||||
_filter = Q()
|
|
||||||
for keyword in q:
|
|
||||||
q_or = Q()
|
|
||||||
q_or |= Q(long_name__icontains=keyword)
|
|
||||||
q_or |= Q(short_name__icontains=keyword)
|
|
||||||
q_or |= Q(parent__long_name__icontains=keyword)
|
|
||||||
q_or |= Q(parent__short_name__icontains=keyword)
|
|
||||||
q_or |= Q(parent__parent__long_name__icontains=keyword)
|
|
||||||
q_or |= Q(parent__parent__short_name__icontains=keyword)
|
|
||||||
_filter.add(q_or, Q.AND)
|
|
||||||
qs = qs.filter(_filter).distinct()
|
|
||||||
return qs
|
|
||||||
|
|
||||||
def get_result_label(self, result):
|
|
||||||
return f"{result.long_name}"
|
|
||||||
|
|
||||||
def get_selected_result_label(self, result):
|
|
||||||
return f"{result.__str__()}"
|
|
@ -1,110 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
||||||
Created on: 18.08.22
|
|
||||||
|
|
||||||
"""
|
|
||||||
import collections
|
|
||||||
|
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
|
||||||
|
|
||||||
from codelist.settings import CODELIST_BIOTOPES_ID, CODELIST_BIOTOPES_EXTRA_CODES_ID
|
|
||||||
from codelist.autocomplete.base import KonovaCodeAutocomplete
|
|
||||||
from konova.utils.message_templates import UNGROUPED
|
|
||||||
|
|
||||||
|
|
||||||
class BiotopeCodeAutocomplete(KonovaCodeAutocomplete):
|
|
||||||
"""
|
|
||||||
Due to limitations of the django dal package, we need to subclass for each code list
|
|
||||||
"""
|
|
||||||
group_by_related = "parent"
|
|
||||||
related_field_name = "long_name"
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.c = CODELIST_BIOTOPES_ID
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def order_by(self, qs):
|
|
||||||
""" Orders by a predefined value
|
|
||||||
|
|
||||||
Wrapped in a function to provide inheritance-based different orders
|
|
||||||
|
|
||||||
Args:
|
|
||||||
qs (QuerySet): The queryset to be ordered
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
qs (QuerySet): The ordered queryset
|
|
||||||
"""
|
|
||||||
return qs.order_by(
|
|
||||||
"short_name",
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_result_label(self, result):
|
|
||||||
return f"{result.long_name} ({result.short_name})"
|
|
||||||
|
|
||||||
def get_results(self, context):
|
|
||||||
"""Return the options grouped by a common related model.
|
|
||||||
|
|
||||||
Raises ImproperlyConfigured if self.group_by_name is not configured
|
|
||||||
"""
|
|
||||||
if not self.group_by_related:
|
|
||||||
raise ImproperlyConfigured("Missing group_by_related.")
|
|
||||||
|
|
||||||
super_groups = collections.OrderedDict()
|
|
||||||
|
|
||||||
object_list = context['object_list']
|
|
||||||
|
|
||||||
for result in object_list:
|
|
||||||
group = result.parent if result.parent else None
|
|
||||||
group_name = f"{group.long_name} ({group.short_name})" if group else UNGROUPED
|
|
||||||
super_group = result.parent.parent if result.parent else None
|
|
||||||
super_group_name = f"{super_group.long_name} ({super_group.short_name})" if super_group else UNGROUPED
|
|
||||||
super_groups.setdefault(super_group_name, {})
|
|
||||||
super_groups[super_group_name].setdefault(group_name, [])
|
|
||||||
super_groups[super_group_name][group_name].append(result)
|
|
||||||
|
|
||||||
return [{
|
|
||||||
'id': None,
|
|
||||||
'text': super_group,
|
|
||||||
'children': [{
|
|
||||||
"id": None,
|
|
||||||
"text": group,
|
|
||||||
"children": [{
|
|
||||||
'id': self.get_result_value(result),
|
|
||||||
'text': self.get_result_label(result),
|
|
||||||
'selected_text': self.get_selected_result_label(result),
|
|
||||||
} for result in results]
|
|
||||||
} for group, results in groups.items()]
|
|
||||||
} for super_group, groups in super_groups.items()]
|
|
||||||
|
|
||||||
|
|
||||||
class BiotopeExtraCodeAutocomplete(KonovaCodeAutocomplete):
|
|
||||||
"""
|
|
||||||
Due to limitations of the django dal package, we need to subclass for each code list
|
|
||||||
"""
|
|
||||||
group_by_related = "parent"
|
|
||||||
related_field_name = "long_name"
|
|
||||||
paginate_by = 200
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.c = CODELIST_BIOTOPES_EXTRA_CODES_ID
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def order_by(self, qs):
|
|
||||||
""" Orders by a predefined value
|
|
||||||
|
|
||||||
Wrapped in a function to provide inheritance-based different orders
|
|
||||||
|
|
||||||
Args:
|
|
||||||
qs (QuerySet): The queryset to be ordered
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
qs (QuerySet): The ordered queryset
|
|
||||||
"""
|
|
||||||
return qs.order_by(
|
|
||||||
"long_name",
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_result_label(self, result):
|
|
||||||
return f"{result.long_name} ({result.short_name})"
|
|
@ -1,45 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
||||||
Created on: 18.08.22
|
|
||||||
|
|
||||||
"""
|
|
||||||
from codelist.settings import CODELIST_COMPENSATION_ACTION_ID, CODELIST_COMPENSATION_ACTION_DETAIL_ID
|
|
||||||
from codelist.autocomplete.base import KonovaCodeAutocomplete
|
|
||||||
|
|
||||||
|
|
||||||
class CompensationActionCodeAutocomplete(KonovaCodeAutocomplete):
|
|
||||||
"""
|
|
||||||
Due to limitations of the django dal package, we need to subclass for each code list
|
|
||||||
"""
|
|
||||||
group_by_related = "parent"
|
|
||||||
related_field_name = "long_name"
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.c = CODELIST_COMPENSATION_ACTION_ID
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def order_by(self, qs):
|
|
||||||
return qs.order_by(
|
|
||||||
"parent__long_name"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class CompensationActionDetailCodeAutocomplete(KonovaCodeAutocomplete):
|
|
||||||
"""
|
|
||||||
Due to limitations of the django dal package, we need to subclass for each code list
|
|
||||||
"""
|
|
||||||
group_by_related = "parent"
|
|
||||||
related_field_name = "long_name"
|
|
||||||
paginate_by = 200
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.c = CODELIST_COMPENSATION_ACTION_DETAIL_ID
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def order_by(self, qs):
|
|
||||||
return qs.order_by(
|
|
||||||
"long_name"
|
|
||||||
)
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
||||||
Created on: 18.08.22
|
|
||||||
|
|
||||||
"""
|
|
||||||
from codelist.settings import CODELIST_HANDLER_ID
|
|
||||||
from codelist.autocomplete.base import KonovaCodeAutocomplete
|
|
||||||
|
|
||||||
|
|
||||||
class HandlerCodeAutocomplete(KonovaCodeAutocomplete):
|
|
||||||
"""
|
|
||||||
Due to limitations of the django dal package, we need to subclass for each code list
|
|
||||||
"""
|
|
||||||
group_by_related = "parent"
|
|
||||||
related_field_name = "long_name"
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.c = CODELIST_HANDLER_ID
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def get_result_label(self, result):
|
|
||||||
return result.long_name
|
|
@ -1,24 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
||||||
Created on: 18.08.22
|
|
||||||
|
|
||||||
"""
|
|
||||||
from codelist.settings import CODELIST_LAW_ID
|
|
||||||
from codelist.autocomplete.base import KonovaCodeAutocomplete
|
|
||||||
|
|
||||||
|
|
||||||
class LawCodeAutocomplete(KonovaCodeAutocomplete):
|
|
||||||
"""
|
|
||||||
Due to limitations of the django dal package, we need to subclass for each code list
|
|
||||||
"""
|
|
||||||
group_by_related = "parent"
|
|
||||||
related_field_name = "long_name"
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.c = CODELIST_LAW_ID
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def get_result_label(self, result):
|
|
||||||
return f"{result.long_name} ({result.short_name})"
|
|
@ -1,41 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
||||||
Created on: 18.08.22
|
|
||||||
|
|
||||||
"""
|
|
||||||
from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID, CODELIST_REGISTRATION_OFFICE_ID
|
|
||||||
from codelist.autocomplete.base import KonovaCodeAutocomplete
|
|
||||||
|
|
||||||
|
|
||||||
class RegistrationOfficeCodeAutocomplete(KonovaCodeAutocomplete):
|
|
||||||
"""
|
|
||||||
Due to limitations of the django dal package, we need to subclass for each code list
|
|
||||||
"""
|
|
||||||
group_by_related = "parent"
|
|
||||||
related_field_name = "long_name"
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.c = CODELIST_REGISTRATION_OFFICE_ID
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def order_by(self, qs):
|
|
||||||
return qs.order_by(
|
|
||||||
"parent__long_name"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ConservationOfficeCodeAutocomplete(KonovaCodeAutocomplete):
|
|
||||||
"""
|
|
||||||
Due to limitations of the django dal package, we need to subclass for each code list
|
|
||||||
"""
|
|
||||||
group_by_related = "parent"
|
|
||||||
related_field_name = "long_name"
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.c = CODELIST_CONSERVATION_OFFICE_ID
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def get_result_label(self, result):
|
|
||||||
return f"{result.long_name} ({result.short_name})"
|
|
@ -1,21 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
||||||
Created on: 18.08.22
|
|
||||||
|
|
||||||
"""
|
|
||||||
from codelist.autocomplete.base import KonovaCodeAutocomplete
|
|
||||||
from codelist.settings import CODELIST_PROCESS_TYPE_ID
|
|
||||||
|
|
||||||
|
|
||||||
class ProcessTypeCodeAutocomplete(KonovaCodeAutocomplete):
|
|
||||||
"""
|
|
||||||
Due to limitations of the django dal package, we need to subclass for each code list
|
|
||||||
"""
|
|
||||||
group_by_related = "parent"
|
|
||||||
related_field_name = "long_name"
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.c = CODELIST_PROCESS_TYPE_ID
|
|
||||||
super().__init__(*args, **kwargs)
|
|
@ -7,24 +7,8 @@ Created on: 23.08.21
|
|||||||
"""
|
"""
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
from codelist.autocomplete.biotope import BiotopeCodeAutocomplete, BiotopeExtraCodeAutocomplete
|
|
||||||
from codelist.autocomplete.compensation_action import CompensationActionDetailCodeAutocomplete, \
|
|
||||||
CompensationActionCodeAutocomplete
|
|
||||||
from codelist.autocomplete.handler import HandlerCodeAutocomplete
|
|
||||||
from codelist.autocomplete.law import LawCodeAutocomplete
|
|
||||||
from codelist.autocomplete.office import ConservationOfficeCodeAutocomplete, RegistrationOfficeCodeAutocomplete
|
|
||||||
from codelist.autocomplete.process_type import ProcessTypeCodeAutocomplete
|
|
||||||
|
|
||||||
app_name = "codelist"
|
app_name = "codelist"
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("atcmplt/codes/biotope", BiotopeCodeAutocomplete.as_view(), name="biotope-autocomplete"),
|
|
||||||
path("atcmplt/codes/biotope/extra", BiotopeExtraCodeAutocomplete.as_view(),
|
|
||||||
name="biotope-extra-type-autocomplete"),
|
|
||||||
path("atcmplt/codes/law", LawCodeAutocomplete.as_view(), name="law-autocomplete"),
|
|
||||||
path("atcmplt/codes/reg-off", RegistrationOfficeCodeAutocomplete.as_view(), name="registration-office-autocomplete"),
|
|
||||||
path("atcmplt/codes/cons-off", ConservationOfficeCodeAutocomplete.as_view(), name="conservation-office-autocomplete"),
|
|
||||||
path("atcmplt/codes/handler", HandlerCodeAutocomplete.as_view(), name="handler-autocomplete"),
|
|
||||||
path("atcmplt/codes/comp/action", CompensationActionCodeAutocomplete.as_view(), name="compensation-action-autocomplete"),
|
|
||||||
path("atcmplt/codes/comp/action/detail", CompensationActionDetailCodeAutocomplete.as_view(), name="compensation-action-detail-autocomplete"),
|
|
||||||
path("atcmplt/codes/prc-type", ProcessTypeCodeAutocomplete.as_view(), name="process-type-autocomplete"),
|
|
||||||
]
|
]
|
@ -1,7 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
||||||
Created on: 18.08.22
|
|
||||||
|
|
||||||
"""
|
|
@ -1,34 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
||||||
Created on: 18.08.22
|
|
||||||
|
|
||||||
"""
|
|
||||||
from dal_select2.views import Select2QuerySetView
|
|
||||||
from django.db.models import Q
|
|
||||||
|
|
||||||
from compensation.models import EcoAccount
|
|
||||||
|
|
||||||
|
|
||||||
class EcoAccountAutocomplete(Select2QuerySetView):
|
|
||||||
""" Autocomplete for ecoAccount entries
|
|
||||||
|
|
||||||
Only returns entries that are already recorded and not deleted
|
|
||||||
|
|
||||||
"""
|
|
||||||
def get_queryset(self):
|
|
||||||
if self.request.user.is_anonymous:
|
|
||||||
return EcoAccount.objects.none()
|
|
||||||
qs = EcoAccount.objects.filter(
|
|
||||||
deleted=None,
|
|
||||||
recorded__isnull=False,
|
|
||||||
).order_by(
|
|
||||||
"identifier"
|
|
||||||
)
|
|
||||||
if self.q:
|
|
||||||
qs = qs.filter(
|
|
||||||
Q(identifier__icontains=self.q) |
|
|
||||||
Q(title__icontains=self.q)
|
|
||||||
).distinct()
|
|
||||||
return qs
|
|
@ -1,14 +1,17 @@
|
|||||||
"""
|
"""
|
||||||
Author: Michel Peltriaux
|
Author: Michel Peltriaux
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||||
Created on: 18.08.22
|
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, Q
|
from django.db.models import QuerySet, Q
|
||||||
|
|
||||||
from konova.filters.table_filters import AbstractTableFilter, CheckboxTableFilter, QueryTableFilter, \
|
from konova.filters.mixins import ConservationOfficeTableFilterMixin
|
||||||
SelectionTableFilter
|
from konova.filters.table_filters import QueryTableFilter, CheckboxTableFilter, SelectionTableFilter, AbstractTableFilter
|
||||||
|
|
||||||
|
|
||||||
class SelectionCompensationTableFilter(SelectionTableFilter):
|
class SelectionCompensationTableFilter(SelectionTableFilter):
|
||||||
@ -111,3 +114,71 @@ class CompensationTableFilter(AbstractTableFilter):
|
|||||||
)
|
)
|
||||||
# Overwrite final queryset as well
|
# Overwrite final queryset as well
|
||||||
self.qs = self.checkbox_filter.qs
|
self.qs = self.checkbox_filter.qs
|
||||||
|
|
||||||
|
|
||||||
|
class CheckboxEcoAccountTableFilter(CheckboxTableFilter):
|
||||||
|
sr = django_filters.BooleanFilter(
|
||||||
|
method='filter_only_show_unrecorded',
|
||||||
|
label=_("Show only unrecorded"),
|
||||||
|
label_suffix=_(""),
|
||||||
|
widget=forms.CheckboxInput(
|
||||||
|
attrs={
|
||||||
|
"class": "form-check-input",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def filter_only_show_unrecorded(self, queryset, name, value) -> QuerySet:
|
||||||
|
""" Filters queryset depending on value of 'show_recorded' setting
|
||||||
|
|
||||||
|
Args:
|
||||||
|
queryset ():
|
||||||
|
name ():
|
||||||
|
value ():
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
if value:
|
||||||
|
return queryset.filter(
|
||||||
|
recorded=None,
|
||||||
|
)
|
||||||
|
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
|
@ -1,7 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
||||||
Created on: 18.08.22
|
|
||||||
|
|
||||||
"""
|
|
@ -1,82 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
||||||
Created on: 18.08.22
|
|
||||||
|
|
||||||
"""
|
|
||||||
from django import forms
|
|
||||||
from django.db.models import QuerySet
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
import django_filters
|
|
||||||
|
|
||||||
from konova.filters.mixins.office import ConservationOfficeTableFilterMixin
|
|
||||||
from konova.filters.table_filters import AbstractTableFilter, CheckboxTableFilter, QueryTableFilter
|
|
||||||
|
|
||||||
|
|
||||||
class CheckboxEcoAccountTableFilter(CheckboxTableFilter):
|
|
||||||
sr = django_filters.BooleanFilter(
|
|
||||||
method='filter_only_show_unrecorded',
|
|
||||||
label=_("Show only unrecorded"),
|
|
||||||
label_suffix=_(""),
|
|
||||||
widget=forms.CheckboxInput(
|
|
||||||
attrs={
|
|
||||||
"class": "form-check-input",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def filter_only_show_unrecorded(self, queryset, name, value) -> QuerySet:
|
|
||||||
""" Filters queryset depending on value of 'show_recorded' setting
|
|
||||||
|
|
||||||
Args:
|
|
||||||
queryset ():
|
|
||||||
name ():
|
|
||||||
value ():
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
if value:
|
|
||||||
return queryset.filter(
|
|
||||||
recorded=None,
|
|
||||||
)
|
|
||||||
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
|
|
@ -88,7 +88,7 @@ class NewCompensationForm(AbstractCompensationForm,
|
|||||||
deleted=None,
|
deleted=None,
|
||||||
),
|
),
|
||||||
widget=autocomplete.ModelSelect2(
|
widget=autocomplete.ModelSelect2(
|
||||||
url="intervention:autocomplete",
|
url="interventions-autocomplete",
|
||||||
attrs={
|
attrs={
|
||||||
"data-placeholder": _("Click for selection"),
|
"data-placeholder": _("Click for selection"),
|
||||||
"data-minimum-input-length": 3,
|
"data-minimum-input-length": 3,
|
||||||
|
@ -27,7 +27,7 @@ class CompensationResponsibleFormMixin(forms.Form):
|
|||||||
code_lists__in=[CODELIST_CONSERVATION_OFFICE_ID],
|
code_lists__in=[CODELIST_CONSERVATION_OFFICE_ID],
|
||||||
),
|
),
|
||||||
widget=autocomplete.ModelSelect2(
|
widget=autocomplete.ModelSelect2(
|
||||||
url="codelist:conservation-office-autocomplete",
|
url="codes-conservation-office-autocomplete",
|
||||||
attrs={
|
attrs={
|
||||||
"data-placeholder": _("Click for selection")
|
"data-placeholder": _("Click for selection")
|
||||||
}
|
}
|
||||||
@ -57,7 +57,7 @@ class CompensationResponsibleFormMixin(forms.Form):
|
|||||||
code_lists__in=[CODELIST_HANDLER_ID],
|
code_lists__in=[CODELIST_HANDLER_ID],
|
||||||
),
|
),
|
||||||
widget=autocomplete.ModelSelect2(
|
widget=autocomplete.ModelSelect2(
|
||||||
url="codelist:handler-autocomplete",
|
url="codes-handler-autocomplete",
|
||||||
attrs={
|
attrs={
|
||||||
"data-placeholder": _("Click for selection"),
|
"data-placeholder": _("Click for selection"),
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ class NewCompensationActionModalForm(BaseModalForm):
|
|||||||
code_lists__in=[CODELIST_COMPENSATION_ACTION_DETAIL_ID],
|
code_lists__in=[CODELIST_COMPENSATION_ACTION_DETAIL_ID],
|
||||||
),
|
),
|
||||||
widget=autocomplete.ModelSelect2Multiple(
|
widget=autocomplete.ModelSelect2Multiple(
|
||||||
url="codelist:compensation-action-detail-autocomplete",
|
url="codes-compensation-action-detail-autocomplete",
|
||||||
attrs={
|
attrs={
|
||||||
"data-placeholder": _("Action Type detail"),
|
"data-placeholder": _("Action Type detail"),
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ class NewCompensationStateModalForm(BaseModalForm):
|
|||||||
code_lists__in=[CODELIST_BIOTOPES_EXTRA_CODES_ID],
|
code_lists__in=[CODELIST_BIOTOPES_EXTRA_CODES_ID],
|
||||||
),
|
),
|
||||||
widget=autocomplete.ModelSelect2Multiple(
|
widget=autocomplete.ModelSelect2Multiple(
|
||||||
url="codelist:biotope-extra-type-autocomplete",
|
url="codes-biotope-extra-type-autocomplete",
|
||||||
attrs={
|
attrs={
|
||||||
"data-placeholder": _("Biotope additional type"),
|
"data-placeholder": _("Biotope additional type"),
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
"""
|
"""
|
||||||
Author: Michel Peltriaux
|
Author: Michel Peltriaux
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||||
Created on: 18.08.22
|
Created on: 01.12.20
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from konova.utils.message_templates import DATA_IS_UNCHECKED, DATA_CHECKED_ON_TEMPLATE, DATA_CHECKED_PREVIOUSLY_TEMPLATE
|
||||||
from django.http import HttpRequest
|
from django.http import HttpRequest
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
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.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from compensation.filters.compensation import CompensationTableFilter
|
from compensation.filters import CompensationTableFilter, EcoAccountTableFilter
|
||||||
from compensation.models import Compensation
|
from compensation.models import Compensation, EcoAccount
|
||||||
from konova.utils.message_templates import DATA_IS_UNCHECKED, DATA_CHECKED_ON_TEMPLATE, DATA_CHECKED_PREVIOUSLY_TEMPLATE
|
|
||||||
from konova.utils.tables import BaseTable, TableRenderMixin
|
from konova.utils.tables import BaseTable, TableRenderMixin
|
||||||
import django_tables2 as tables
|
import django_tables2 as tables
|
||||||
|
|
||||||
@ -187,3 +187,160 @@ class CompensationTable(BaseTable, TableRenderMixin):
|
|||||||
icn_class="fas fa-edit rlp-r-inv" if has_access else "far fa-edit",
|
icn_class="fas fa-edit rlp-r-inv" if has_access else "far fa-edit",
|
||||||
)
|
)
|
||||||
return format_html(html)
|
return format_html(html)
|
||||||
|
|
||||||
|
|
||||||
|
class EcoAccountTable(BaseTable, TableRenderMixin):
|
||||||
|
id = tables.Column(
|
||||||
|
verbose_name=_("Identifier"),
|
||||||
|
orderable=True,
|
||||||
|
accessor="identifier",
|
||||||
|
)
|
||||||
|
t = tables.Column(
|
||||||
|
verbose_name=_("Title"),
|
||||||
|
orderable=True,
|
||||||
|
accessor="title",
|
||||||
|
)
|
||||||
|
d = tables.Column(
|
||||||
|
verbose_name=_("Parcel gmrkng"),
|
||||||
|
orderable=True,
|
||||||
|
accessor="geometry",
|
||||||
|
)
|
||||||
|
av = tables.Column(
|
||||||
|
verbose_name=_("Available"),
|
||||||
|
orderable=True,
|
||||||
|
empty_values=[],
|
||||||
|
attrs={
|
||||||
|
"th": {
|
||||||
|
"class": "w-20",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
r = tables.Column(
|
||||||
|
verbose_name=_("Recorded"),
|
||||||
|
orderable=True,
|
||||||
|
empty_values=[],
|
||||||
|
accessor="recorded",
|
||||||
|
)
|
||||||
|
e = tables.Column(
|
||||||
|
verbose_name=_("Editable"),
|
||||||
|
orderable=True,
|
||||||
|
empty_values=[],
|
||||||
|
accessor="users",
|
||||||
|
)
|
||||||
|
lm = tables.Column(
|
||||||
|
verbose_name=_("Last edit"),
|
||||||
|
orderable=True,
|
||||||
|
accessor="modified__timestamp",
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta(BaseTable.Meta):
|
||||||
|
template_name = "django_tables2/bootstrap4.html"
|
||||||
|
|
||||||
|
def __init__(self, request: HttpRequest, *args, **kwargs):
|
||||||
|
self.title = _("Eco Accounts")
|
||||||
|
self.add_new_url = reverse("compensation:acc:new")
|
||||||
|
qs = kwargs.get("queryset", None)
|
||||||
|
self.filter = EcoAccountTableFilter(
|
||||||
|
user=request.user,
|
||||||
|
data=request.GET,
|
||||||
|
queryset=qs,
|
||||||
|
)
|
||||||
|
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
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value (str): The identifier value
|
||||||
|
record (EcoAccount): The eco account record
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
html = ""
|
||||||
|
html += self.render_link(
|
||||||
|
tooltip=_("Open {}").format(_("Eco-account")),
|
||||||
|
href=reverse("compensation:acc:detail", args=(record.id,)),
|
||||||
|
txt=value,
|
||||||
|
new_tab=False,
|
||||||
|
)
|
||||||
|
return format_html(html)
|
||||||
|
|
||||||
|
def render_av(self, value, record: EcoAccount):
|
||||||
|
""" Renders the available column for an eco account
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value (str): The identifier value
|
||||||
|
record (EcoAccount): The eco account record
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
value_total, value_relative = record.get_available_rest()
|
||||||
|
html = render_to_string("konova/widgets/progressbar.html", {"value": value_relative})
|
||||||
|
return format_html(html)
|
||||||
|
|
||||||
|
def render_d(self, value, record: Compensation):
|
||||||
|
""" Renders the parcel district column for a compensation
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value (str): The geometry
|
||||||
|
record (Compensation): The compensation record
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
parcels = value.get_underlying_parcels().values_list(
|
||||||
|
"parcel_group__name",
|
||||||
|
flat=True
|
||||||
|
).distinct()
|
||||||
|
html = render_to_string(
|
||||||
|
"table/gmrkng_col.html",
|
||||||
|
{
|
||||||
|
"entries": parcels
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return html
|
||||||
|
|
||||||
|
def render_r(self, value, record: EcoAccount):
|
||||||
|
""" Renders the recorded column for an eco account
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value (str): The identifier value
|
||||||
|
record (EcoAccount): The eco account record
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
html = ""
|
||||||
|
checked = value is not None
|
||||||
|
tooltip = _("Not recorded yet. Can not be used for deductions, yet.")
|
||||||
|
if checked:
|
||||||
|
on = value.get_timestamp_str_formatted()
|
||||||
|
tooltip = _("Recorded on {} by {}").format(on, record.recorded.user)
|
||||||
|
html += self.render_bookmark(
|
||||||
|
tooltip=tooltip,
|
||||||
|
icn_filled=checked,
|
||||||
|
)
|
||||||
|
return format_html(html)
|
||||||
|
|
||||||
|
def render_e(self, value, record: EcoAccount):
|
||||||
|
""" Renders the editable column for an eco account
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value (str): The identifier value
|
||||||
|
record (EcoAccount): The eco account record
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
html = ""
|
||||||
|
# Do not use value in here, since value does use unprefetched 'users' manager, where record has already
|
||||||
|
# prefetched users data
|
||||||
|
has_access = record.is_shared_with(self.user)
|
||||||
|
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)
|
@ -1,7 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
||||||
Created on: 18.08.22
|
|
||||||
|
|
||||||
"""
|
|
@ -1,175 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
||||||
Created on: 18.08.22
|
|
||||||
|
|
||||||
"""
|
|
||||||
from django.http import HttpRequest
|
|
||||||
from django.template.loader import render_to_string
|
|
||||||
from django.urls import reverse
|
|
||||||
from django.utils.html import format_html
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
|
|
||||||
from compensation.filters.eco_account import EcoAccountTableFilter
|
|
||||||
from compensation.models import EcoAccount
|
|
||||||
from konova.utils.tables import TableRenderMixin, BaseTable
|
|
||||||
|
|
||||||
import django_tables2 as tables
|
|
||||||
|
|
||||||
|
|
||||||
class EcoAccountTable(BaseTable, TableRenderMixin):
|
|
||||||
id = tables.Column(
|
|
||||||
verbose_name=_("Identifier"),
|
|
||||||
orderable=True,
|
|
||||||
accessor="identifier",
|
|
||||||
)
|
|
||||||
t = tables.Column(
|
|
||||||
verbose_name=_("Title"),
|
|
||||||
orderable=True,
|
|
||||||
accessor="title",
|
|
||||||
)
|
|
||||||
d = tables.Column(
|
|
||||||
verbose_name=_("Parcel gmrkng"),
|
|
||||||
orderable=True,
|
|
||||||
accessor="geometry",
|
|
||||||
)
|
|
||||||
av = tables.Column(
|
|
||||||
verbose_name=_("Available"),
|
|
||||||
orderable=True,
|
|
||||||
empty_values=[],
|
|
||||||
attrs={
|
|
||||||
"th": {
|
|
||||||
"class": "w-20",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
r = tables.Column(
|
|
||||||
verbose_name=_("Recorded"),
|
|
||||||
orderable=True,
|
|
||||||
empty_values=[],
|
|
||||||
accessor="recorded",
|
|
||||||
)
|
|
||||||
e = tables.Column(
|
|
||||||
verbose_name=_("Editable"),
|
|
||||||
orderable=True,
|
|
||||||
empty_values=[],
|
|
||||||
accessor="users",
|
|
||||||
)
|
|
||||||
lm = tables.Column(
|
|
||||||
verbose_name=_("Last edit"),
|
|
||||||
orderable=True,
|
|
||||||
accessor="modified__timestamp",
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
|
||||||
template_name = "django_tables2/bootstrap4.html"
|
|
||||||
|
|
||||||
def __init__(self, request: HttpRequest, *args, **kwargs):
|
|
||||||
self.title = _("Eco Accounts")
|
|
||||||
self.add_new_url = reverse("compensation:acc:new")
|
|
||||||
qs = kwargs.get("queryset", None)
|
|
||||||
self.filter = EcoAccountTableFilter(
|
|
||||||
user=request.user,
|
|
||||||
data=request.GET,
|
|
||||||
queryset=qs,
|
|
||||||
)
|
|
||||||
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
|
|
||||||
|
|
||||||
Args:
|
|
||||||
value (str): The identifier value
|
|
||||||
record (EcoAccount): The eco account record
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
html = ""
|
|
||||||
html += self.render_link(
|
|
||||||
tooltip=_("Open {}").format(_("Eco-account")),
|
|
||||||
href=reverse("compensation:acc:detail", args=(record.id,)),
|
|
||||||
txt=value,
|
|
||||||
new_tab=False,
|
|
||||||
)
|
|
||||||
return format_html(html)
|
|
||||||
|
|
||||||
def render_av(self, value, record: EcoAccount):
|
|
||||||
""" Renders the available column for an eco account
|
|
||||||
|
|
||||||
Args:
|
|
||||||
value (str): The identifier value
|
|
||||||
record (EcoAccount): The eco account record
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
value_total, value_relative = record.get_available_rest()
|
|
||||||
html = render_to_string("konova/widgets/progressbar.html", {"value": value_relative})
|
|
||||||
return format_html(html)
|
|
||||||
|
|
||||||
def render_d(self, value, record):
|
|
||||||
""" Renders the parcel district column for a compensation
|
|
||||||
|
|
||||||
Args:
|
|
||||||
value (str): The geometry
|
|
||||||
record (Compensation): The compensation record
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
parcels = value.get_underlying_parcels().values_list(
|
|
||||||
"parcel_group__name",
|
|
||||||
flat=True
|
|
||||||
).distinct()
|
|
||||||
html = render_to_string(
|
|
||||||
"table/gmrkng_col.html",
|
|
||||||
{
|
|
||||||
"entries": parcels
|
|
||||||
}
|
|
||||||
)
|
|
||||||
return html
|
|
||||||
|
|
||||||
def render_r(self, value, record: EcoAccount):
|
|
||||||
""" Renders the recorded column for an eco account
|
|
||||||
|
|
||||||
Args:
|
|
||||||
value (str): The identifier value
|
|
||||||
record (EcoAccount): The eco account record
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
html = ""
|
|
||||||
checked = value is not None
|
|
||||||
tooltip = _("Not recorded yet. Can not be used for deductions, yet.")
|
|
||||||
if checked:
|
|
||||||
on = value.get_timestamp_str_formatted()
|
|
||||||
tooltip = _("Recorded on {} by {}").format(on, record.recorded.user)
|
|
||||||
html += self.render_bookmark(
|
|
||||||
tooltip=tooltip,
|
|
||||||
icn_filled=checked,
|
|
||||||
)
|
|
||||||
return format_html(html)
|
|
||||||
|
|
||||||
def render_e(self, value, record: EcoAccount):
|
|
||||||
""" Renders the editable column for an eco account
|
|
||||||
|
|
||||||
Args:
|
|
||||||
value (str): The identifier value
|
|
||||||
record (EcoAccount): The eco account record
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
html = ""
|
|
||||||
# Do not use value in here, since value does use unprefetched 'users' manager, where record has already
|
|
||||||
# prefetched users data
|
|
||||||
has_access = record.is_shared_with(self.user)
|
|
||||||
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)
|
|
@ -6,8 +6,6 @@ Created on: 24.08.21
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
from compensation.autocomplete.eco_account import EcoAccountAutocomplete
|
|
||||||
from compensation.views.eco_account import *
|
from compensation.views.eco_account import *
|
||||||
|
|
||||||
app_name = "acc"
|
app_name = "acc"
|
||||||
@ -49,6 +47,4 @@ urlpatterns = [
|
|||||||
path('<id>/deduction/<deduction_id>/edit', deduction_edit_view, name='edit-deduction'),
|
path('<id>/deduction/<deduction_id>/edit', deduction_edit_view, name='edit-deduction'),
|
||||||
path('<id>/deduct/new', new_deduction_view, name='new-deduction'),
|
path('<id>/deduct/new', new_deduction_view, name='new-deduction'),
|
||||||
|
|
||||||
# Autocomplete
|
|
||||||
path("atcmplt/eco-accounts", EcoAccountAutocomplete.as_view(), name="autocomplete"),
|
|
||||||
]
|
]
|
@ -12,7 +12,7 @@ from compensation.forms.modals.compensation_action import NewCompensationActionM
|
|||||||
EditCompensationActionModalForm, RemoveCompensationActionModalForm
|
EditCompensationActionModalForm, RemoveCompensationActionModalForm
|
||||||
from compensation.forms.modals.deadline import NewDeadlineModalForm, EditDeadlineModalForm
|
from compensation.forms.modals.deadline import NewDeadlineModalForm, EditDeadlineModalForm
|
||||||
from compensation.models import Compensation, CompensationState, CompensationAction, CompensationDocument
|
from compensation.models import Compensation, CompensationState, CompensationAction, CompensationDocument
|
||||||
from compensation.tables.compensation import CompensationTable
|
from compensation.tables import CompensationTable
|
||||||
from intervention.models import Intervention
|
from intervention.models import Intervention
|
||||||
from konova.contexts import BaseContext
|
from konova.contexts import BaseContext
|
||||||
from konova.decorators import *
|
from konova.decorators import *
|
||||||
|
@ -22,7 +22,7 @@ from compensation.forms.modals.document import NewEcoAccountDocumentModalForm
|
|||||||
from compensation.forms.modals.state import NewCompensationStateModalForm, RemoveCompensationStateModalForm, \
|
from compensation.forms.modals.state import NewCompensationStateModalForm, RemoveCompensationStateModalForm, \
|
||||||
EditCompensationStateModalForm
|
EditCompensationStateModalForm
|
||||||
from compensation.models import EcoAccount, EcoAccountDocument, CompensationState, CompensationAction
|
from compensation.models import EcoAccount, EcoAccountDocument, CompensationState, CompensationAction
|
||||||
from compensation.tables.eco_account import EcoAccountTable
|
from compensation.tables import EcoAccountTable
|
||||||
from intervention.forms.modals.deduction import RemoveEcoAccountDeductionModalForm, NewEcoAccountDeductionModalForm, \
|
from intervention.forms.modals.deduction import RemoveEcoAccountDeductionModalForm, NewEcoAccountDeductionModalForm, \
|
||||||
EditEcoAccountDeductionModalForm
|
EditEcoAccountDeductionModalForm
|
||||||
from intervention.forms.modals.share import ShareModalForm
|
from intervention.forms.modals.share import ShareModalForm
|
||||||
|
@ -5,7 +5,7 @@ Contact: michel.peltriaux@sgdnord.rlp.de
|
|||||||
Created on: 19.08.21
|
Created on: 19.08.21
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from compensation.filters.eco_account import EcoAccountTableFilter
|
from compensation.filters import EcoAccountTableFilter
|
||||||
|
|
||||||
|
|
||||||
class EmaTableFilter(EcoAccountTableFilter):
|
class EmaTableFilter(EcoAccountTableFilter):
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
||||||
Created on: 18.08.22
|
|
||||||
|
|
||||||
"""
|
|
@ -1,36 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
||||||
Created on: 18.08.22
|
|
||||||
|
|
||||||
"""
|
|
||||||
from dal_select2.views import Select2QuerySetView
|
|
||||||
from django.db.models import Q
|
|
||||||
|
|
||||||
from intervention.models import Intervention
|
|
||||||
|
|
||||||
|
|
||||||
class InterventionAutocomplete(Select2QuerySetView):
|
|
||||||
""" Autocomplete for intervention entries
|
|
||||||
|
|
||||||
Only returns entries that are accessible for the requesting user
|
|
||||||
|
|
||||||
"""
|
|
||||||
def get_queryset(self):
|
|
||||||
user = self.request.user
|
|
||||||
if user.is_anonymous:
|
|
||||||
return Intervention.objects.none()
|
|
||||||
qs = Intervention.objects.filter(
|
|
||||||
Q(deleted=None) &
|
|
||||||
Q(users__in=[user]) |
|
|
||||||
Q(teams__in=user.teams.all())
|
|
||||||
).order_by(
|
|
||||||
"identifier"
|
|
||||||
).distinct()
|
|
||||||
if self.q:
|
|
||||||
qs = qs.filter(
|
|
||||||
Q(identifier__icontains=self.q) |
|
|
||||||
Q(title__icontains=self.q)
|
|
||||||
).distinct()
|
|
||||||
return qs
|
|
@ -60,7 +60,7 @@ class NewInterventionForm(BaseForm):
|
|||||||
code_lists__in=[CODELIST_PROCESS_TYPE_ID],
|
code_lists__in=[CODELIST_PROCESS_TYPE_ID],
|
||||||
),
|
),
|
||||||
widget=autocomplete.ModelSelect2(
|
widget=autocomplete.ModelSelect2(
|
||||||
url="codelist:process-type-autocomplete",
|
url="codes-process-type-autocomplete",
|
||||||
attrs={
|
attrs={
|
||||||
"data-placeholder": _("Click for selection"),
|
"data-placeholder": _("Click for selection"),
|
||||||
}
|
}
|
||||||
@ -77,7 +77,7 @@ class NewInterventionForm(BaseForm):
|
|||||||
code_lists__in=[CODELIST_LAW_ID],
|
code_lists__in=[CODELIST_LAW_ID],
|
||||||
),
|
),
|
||||||
widget=autocomplete.ModelSelect2Multiple(
|
widget=autocomplete.ModelSelect2Multiple(
|
||||||
url="codelist:law-autocomplete",
|
url="codes-law-autocomplete",
|
||||||
attrs={
|
attrs={
|
||||||
"data-placeholder": _("Click for selection"),
|
"data-placeholder": _("Click for selection"),
|
||||||
}
|
}
|
||||||
@ -93,7 +93,7 @@ class NewInterventionForm(BaseForm):
|
|||||||
code_lists__in=[CODELIST_REGISTRATION_OFFICE_ID],
|
code_lists__in=[CODELIST_REGISTRATION_OFFICE_ID],
|
||||||
),
|
),
|
||||||
widget=autocomplete.ModelSelect2(
|
widget=autocomplete.ModelSelect2(
|
||||||
url="codelist:registration-office-autocomplete",
|
url="codes-registration-office-autocomplete",
|
||||||
attrs={
|
attrs={
|
||||||
"data-placeholder": _("Click for selection"),
|
"data-placeholder": _("Click for selection"),
|
||||||
}
|
}
|
||||||
@ -109,7 +109,7 @@ class NewInterventionForm(BaseForm):
|
|||||||
code_lists__in=[CODELIST_CONSERVATION_OFFICE_ID],
|
code_lists__in=[CODELIST_CONSERVATION_OFFICE_ID],
|
||||||
),
|
),
|
||||||
widget=autocomplete.ModelSelect2(
|
widget=autocomplete.ModelSelect2(
|
||||||
url="codelist:conservation-office-autocomplete",
|
url="codes-conservation-office-autocomplete",
|
||||||
attrs={
|
attrs={
|
||||||
"data-placeholder": _("Click for selection"),
|
"data-placeholder": _("Click for selection"),
|
||||||
}
|
}
|
||||||
@ -150,7 +150,7 @@ class NewInterventionForm(BaseForm):
|
|||||||
code_lists__in=[CODELIST_HANDLER_ID],
|
code_lists__in=[CODELIST_HANDLER_ID],
|
||||||
),
|
),
|
||||||
widget=autocomplete.ModelSelect2(
|
widget=autocomplete.ModelSelect2(
|
||||||
url="codelist:handler-autocomplete",
|
url="codes-handler-autocomplete",
|
||||||
attrs={
|
attrs={
|
||||||
"data-placeholder": _("Click for selection"),
|
"data-placeholder": _("Click for selection"),
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ class NewEcoAccountDeductionModalForm(BaseModalForm):
|
|||||||
help_text=_("Only recorded accounts can be selected for deductions"),
|
help_text=_("Only recorded accounts can be selected for deductions"),
|
||||||
queryset=EcoAccount.objects.filter(deleted=None),
|
queryset=EcoAccount.objects.filter(deleted=None),
|
||||||
widget=autocomplete.ModelSelect2(
|
widget=autocomplete.ModelSelect2(
|
||||||
url="compensation:acc:autocomplete",
|
url="accounts-autocomplete",
|
||||||
attrs={
|
attrs={
|
||||||
"data-placeholder": _("Eco-account"),
|
"data-placeholder": _("Eco-account"),
|
||||||
"data-minimum-input-length": 3,
|
"data-minimum-input-length": 3,
|
||||||
@ -60,7 +60,7 @@ class NewEcoAccountDeductionModalForm(BaseModalForm):
|
|||||||
help_text=_("Only shared interventions can be selected"),
|
help_text=_("Only shared interventions can be selected"),
|
||||||
queryset=Intervention.objects.filter(deleted=None),
|
queryset=Intervention.objects.filter(deleted=None),
|
||||||
widget=autocomplete.ModelSelect2(
|
widget=autocomplete.ModelSelect2(
|
||||||
url="intervention:autocomplete",
|
url="interventions-autocomplete",
|
||||||
attrs={
|
attrs={
|
||||||
"data-placeholder": _("Intervention"),
|
"data-placeholder": _("Intervention"),
|
||||||
"data-minimum-input-length": 3,
|
"data-minimum-input-length": 3,
|
||||||
|
@ -36,7 +36,7 @@ class ShareModalForm(BaseModalForm):
|
|||||||
required=False,
|
required=False,
|
||||||
queryset=Team.objects.all(),
|
queryset=Team.objects.all(),
|
||||||
widget=autocomplete.ModelSelect2Multiple(
|
widget=autocomplete.ModelSelect2Multiple(
|
||||||
url="user:share-team-autocomplete",
|
url="share-team-autocomplete",
|
||||||
attrs={
|
attrs={
|
||||||
"data-placeholder": _("Click for selection"),
|
"data-placeholder": _("Click for selection"),
|
||||||
"data-minimum-input-length": 3,
|
"data-minimum-input-length": 3,
|
||||||
@ -50,7 +50,7 @@ class ShareModalForm(BaseModalForm):
|
|||||||
required=False,
|
required=False,
|
||||||
queryset=User.objects.all(),
|
queryset=User.objects.all(),
|
||||||
widget=autocomplete.ModelSelect2Multiple(
|
widget=autocomplete.ModelSelect2Multiple(
|
||||||
url="user:share-user-autocomplete",
|
url="share-user-autocomplete",
|
||||||
attrs={
|
attrs={
|
||||||
"data-placeholder": _("Click for selection"),
|
"data-placeholder": _("Click for selection"),
|
||||||
"data-minimum-input-length": 3,
|
"data-minimum-input-length": 3,
|
||||||
|
@ -7,7 +7,6 @@ Created on: 30.11.20
|
|||||||
"""
|
"""
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
from intervention.autocomplete.intervention import InterventionAutocomplete
|
|
||||||
from intervention.views import index_view, new_view, detail_view, edit_view, remove_view, new_document_view, share_view, \
|
from intervention.views import index_view, new_view, detail_view, edit_view, remove_view, new_document_view, share_view, \
|
||||||
create_share_view, remove_revocation_view, new_revocation_view, check_view, log_view, new_deduction_view, \
|
create_share_view, remove_revocation_view, new_revocation_view, check_view, log_view, new_deduction_view, \
|
||||||
record_view, remove_document_view, get_document_view, get_revocation_view, new_id_view, report_view, \
|
record_view, remove_document_view, get_document_view, get_revocation_view, new_id_view, report_view, \
|
||||||
@ -49,7 +48,4 @@ urlpatterns = [
|
|||||||
path('<id>/revocation/<revocation_id>/edit', edit_revocation_view, name='edit-revocation'),
|
path('<id>/revocation/<revocation_id>/edit', edit_revocation_view, name='edit-revocation'),
|
||||||
path('<id>/revocation/<revocation_id>/remove', remove_revocation_view, name='remove-revocation'),
|
path('<id>/revocation/<revocation_id>/remove', remove_revocation_view, name='remove-revocation'),
|
||||||
path('revocation/<doc_id>', get_revocation_view, name='get-doc-revocation'),
|
path('revocation/<doc_id>', get_revocation_view, name='get-doc-revocation'),
|
||||||
|
|
||||||
# Autocomplete
|
|
||||||
path("atcmplt/interventions", InterventionAutocomplete.as_view(), name="autocomplete"),
|
|
||||||
]
|
]
|
401
konova/autocompletes.py
Normal file
401
konova/autocompletes.py
Normal file
@ -0,0 +1,401 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||||
|
Created on: 07.12.20
|
||||||
|
|
||||||
|
"""
|
||||||
|
import collections
|
||||||
|
|
||||||
|
from dal_select2.views import Select2QuerySetView, Select2GroupQuerySetView
|
||||||
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
|
|
||||||
|
from konova.utils.message_templates import UNGROUPED
|
||||||
|
from user.models import User, Team
|
||||||
|
from django.db.models import Q
|
||||||
|
|
||||||
|
from codelist.models import KonovaCode
|
||||||
|
from codelist.settings import CODELIST_COMPENSATION_ACTION_ID, CODELIST_BIOTOPES_ID, CODELIST_LAW_ID, \
|
||||||
|
CODELIST_REGISTRATION_OFFICE_ID, CODELIST_CONSERVATION_OFFICE_ID, CODELIST_PROCESS_TYPE_ID, \
|
||||||
|
CODELIST_BIOTOPES_EXTRA_CODES_ID, CODELIST_COMPENSATION_ACTION_DETAIL_ID, CODELIST_HANDLER_ID
|
||||||
|
from compensation.models import EcoAccount
|
||||||
|
from intervention.models import Intervention
|
||||||
|
|
||||||
|
|
||||||
|
class EcoAccountAutocomplete(Select2QuerySetView):
|
||||||
|
""" Autocomplete for ecoAccount entries
|
||||||
|
|
||||||
|
Only returns entries that are already recorded and not deleted
|
||||||
|
|
||||||
|
"""
|
||||||
|
def get_queryset(self):
|
||||||
|
if self.request.user.is_anonymous:
|
||||||
|
return EcoAccount.objects.none()
|
||||||
|
qs = EcoAccount.objects.filter(
|
||||||
|
deleted=None,
|
||||||
|
recorded__isnull=False,
|
||||||
|
).order_by(
|
||||||
|
"identifier"
|
||||||
|
)
|
||||||
|
if self.q:
|
||||||
|
qs = qs.filter(
|
||||||
|
Q(identifier__icontains=self.q) |
|
||||||
|
Q(title__icontains=self.q)
|
||||||
|
).distinct()
|
||||||
|
return qs
|
||||||
|
|
||||||
|
|
||||||
|
class InterventionAutocomplete(Select2QuerySetView):
|
||||||
|
""" Autocomplete for intervention entries
|
||||||
|
|
||||||
|
Only returns entries that are accessible for the requesting user
|
||||||
|
|
||||||
|
"""
|
||||||
|
def get_queryset(self):
|
||||||
|
user = self.request.user
|
||||||
|
if user.is_anonymous:
|
||||||
|
return Intervention.objects.none()
|
||||||
|
qs = Intervention.objects.filter(
|
||||||
|
Q(deleted=None) &
|
||||||
|
Q(users__in=[user]) |
|
||||||
|
Q(teams__in=user.teams.all())
|
||||||
|
).order_by(
|
||||||
|
"identifier"
|
||||||
|
).distinct()
|
||||||
|
if self.q:
|
||||||
|
qs = qs.filter(
|
||||||
|
Q(identifier__icontains=self.q) |
|
||||||
|
Q(title__icontains=self.q)
|
||||||
|
).distinct()
|
||||||
|
return qs
|
||||||
|
|
||||||
|
|
||||||
|
class ShareUserAutocomplete(Select2QuerySetView):
|
||||||
|
""" Autocomplete for share with single users
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
def get_queryset(self):
|
||||||
|
if self.request.user.is_anonymous:
|
||||||
|
return User.objects.none()
|
||||||
|
qs = User.objects.all()
|
||||||
|
if self.q:
|
||||||
|
# Due to privacy concerns only a full username match will return the proper user entry
|
||||||
|
qs = qs.filter(
|
||||||
|
Q(username=self.q) |
|
||||||
|
Q(email=self.q)
|
||||||
|
).distinct()
|
||||||
|
qs = qs.order_by("username")
|
||||||
|
return qs
|
||||||
|
|
||||||
|
|
||||||
|
class ShareTeamAutocomplete(Select2QuerySetView):
|
||||||
|
""" Autocomplete for share with teams
|
||||||
|
|
||||||
|
"""
|
||||||
|
def get_queryset(self):
|
||||||
|
if self.request.user.is_anonymous:
|
||||||
|
return Team.objects.none()
|
||||||
|
qs = Team.objects.filter(
|
||||||
|
deleted__isnull=True
|
||||||
|
)
|
||||||
|
if self.q:
|
||||||
|
# Due to privacy concerns only a full username match will return the proper user entry
|
||||||
|
qs = qs.filter(
|
||||||
|
name__icontains=self.q
|
||||||
|
)
|
||||||
|
qs = qs.order_by(
|
||||||
|
"name"
|
||||||
|
)
|
||||||
|
return qs
|
||||||
|
|
||||||
|
|
||||||
|
class TeamAdminAutocomplete(Select2QuerySetView):
|
||||||
|
""" Autocomplete for share with teams
|
||||||
|
|
||||||
|
"""
|
||||||
|
def get_queryset(self):
|
||||||
|
if self.request.user.is_anonymous:
|
||||||
|
return User.objects.none()
|
||||||
|
qs = User.objects.filter(
|
||||||
|
id__in=self.forwarded.get("members", [])
|
||||||
|
).exclude(
|
||||||
|
id__in=self.forwarded.get("admins", [])
|
||||||
|
)
|
||||||
|
if self.q:
|
||||||
|
# Due to privacy concerns only a full username match will return the proper user entry
|
||||||
|
qs = qs.filter(
|
||||||
|
name__icontains=self.q
|
||||||
|
)
|
||||||
|
qs = qs.order_by(
|
||||||
|
"username"
|
||||||
|
)
|
||||||
|
return qs
|
||||||
|
|
||||||
|
|
||||||
|
class KonovaCodeAutocomplete(Select2GroupQuerySetView):
|
||||||
|
"""
|
||||||
|
Provides simple autocomplete functionality for codes
|
||||||
|
|
||||||
|
Parameter support:
|
||||||
|
* q: Search for a word inside long_name of a code
|
||||||
|
* c: Search inside a special codelist
|
||||||
|
|
||||||
|
"""
|
||||||
|
paginate_by = 50
|
||||||
|
|
||||||
|
def order_by(self, qs):
|
||||||
|
""" Orders by a predefined value
|
||||||
|
|
||||||
|
Wrapped in a function to provide inheritance-based different orders
|
||||||
|
|
||||||
|
Args:
|
||||||
|
qs (QuerySet): The queryset to be ordered
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
qs (QuerySet): The ordered queryset
|
||||||
|
"""
|
||||||
|
return qs.order_by(
|
||||||
|
"long_name"
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
if self.request.user.is_anonymous:
|
||||||
|
return KonovaCode.objects.none()
|
||||||
|
qs = KonovaCode.objects.filter(
|
||||||
|
is_archived=False,
|
||||||
|
is_selectable=True,
|
||||||
|
is_leaf=True,
|
||||||
|
)
|
||||||
|
qs = self.order_by(qs)
|
||||||
|
if self.c:
|
||||||
|
qs = qs.filter(
|
||||||
|
code_lists__in=[self.c]
|
||||||
|
)
|
||||||
|
if self.q:
|
||||||
|
# Remove whitespaces from self.q and split input in all keywords (if multiple given)
|
||||||
|
q = dict.fromkeys(self.q.strip().split(" "))
|
||||||
|
# Create one filter looking up for all keys where all keywords can be found in the same result
|
||||||
|
_filter = Q()
|
||||||
|
for keyword in q:
|
||||||
|
q_or = Q()
|
||||||
|
q_or |= Q(long_name__icontains=keyword)
|
||||||
|
q_or |= Q(short_name__icontains=keyword)
|
||||||
|
q_or |= Q(parent__long_name__icontains=keyword)
|
||||||
|
q_or |= Q(parent__short_name__icontains=keyword)
|
||||||
|
q_or |= Q(parent__parent__long_name__icontains=keyword)
|
||||||
|
q_or |= Q(parent__parent__short_name__icontains=keyword)
|
||||||
|
_filter.add(q_or, Q.AND)
|
||||||
|
qs = qs.filter(_filter).distinct()
|
||||||
|
return qs
|
||||||
|
|
||||||
|
def get_result_label(self, result):
|
||||||
|
return f"{result.long_name}"
|
||||||
|
|
||||||
|
def get_selected_result_label(self, result):
|
||||||
|
return f"{result.__str__()}"
|
||||||
|
|
||||||
|
|
||||||
|
class CompensationActionCodeAutocomplete(KonovaCodeAutocomplete):
|
||||||
|
"""
|
||||||
|
Due to limitations of the django dal package, we need to subclass for each code list
|
||||||
|
"""
|
||||||
|
group_by_related = "parent"
|
||||||
|
related_field_name = "long_name"
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.c = CODELIST_COMPENSATION_ACTION_ID
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def order_by(self, qs):
|
||||||
|
return qs.order_by(
|
||||||
|
"parent__long_name"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CompensationActionDetailCodeAutocomplete(KonovaCodeAutocomplete):
|
||||||
|
"""
|
||||||
|
Due to limitations of the django dal package, we need to subclass for each code list
|
||||||
|
"""
|
||||||
|
group_by_related = "parent"
|
||||||
|
related_field_name = "long_name"
|
||||||
|
paginate_by = 200
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.c = CODELIST_COMPENSATION_ACTION_DETAIL_ID
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def order_by(self, qs):
|
||||||
|
return qs.order_by(
|
||||||
|
"long_name"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class BiotopeCodeAutocomplete(KonovaCodeAutocomplete):
|
||||||
|
"""
|
||||||
|
Due to limitations of the django dal package, we need to subclass for each code list
|
||||||
|
"""
|
||||||
|
group_by_related = "parent"
|
||||||
|
related_field_name = "long_name"
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.c = CODELIST_BIOTOPES_ID
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def order_by(self, qs):
|
||||||
|
""" Orders by a predefined value
|
||||||
|
|
||||||
|
Wrapped in a function to provide inheritance-based different orders
|
||||||
|
|
||||||
|
Args:
|
||||||
|
qs (QuerySet): The queryset to be ordered
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
qs (QuerySet): The ordered queryset
|
||||||
|
"""
|
||||||
|
return qs.order_by(
|
||||||
|
"short_name",
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_result_label(self, result):
|
||||||
|
return f"{result.long_name} ({result.short_name})"
|
||||||
|
|
||||||
|
def get_results(self, context):
|
||||||
|
"""Return the options grouped by a common related model.
|
||||||
|
|
||||||
|
Raises ImproperlyConfigured if self.group_by_name is not configured
|
||||||
|
"""
|
||||||
|
if not self.group_by_related:
|
||||||
|
raise ImproperlyConfigured("Missing group_by_related.")
|
||||||
|
|
||||||
|
super_groups = collections.OrderedDict()
|
||||||
|
|
||||||
|
object_list = context['object_list']
|
||||||
|
|
||||||
|
for result in object_list:
|
||||||
|
group = result.parent if result.parent else None
|
||||||
|
group_name = f"{group.long_name} ({group.short_name})" if group else UNGROUPED
|
||||||
|
super_group = result.parent.parent if result.parent else None
|
||||||
|
super_group_name = f"{super_group.long_name} ({super_group.short_name})" if super_group else UNGROUPED
|
||||||
|
super_groups.setdefault(super_group_name, {})
|
||||||
|
super_groups[super_group_name].setdefault(group_name, [])
|
||||||
|
super_groups[super_group_name][group_name].append(result)
|
||||||
|
|
||||||
|
return [{
|
||||||
|
'id': None,
|
||||||
|
'text': super_group,
|
||||||
|
'children': [{
|
||||||
|
"id": None,
|
||||||
|
"text": group,
|
||||||
|
"children": [{
|
||||||
|
'id': self.get_result_value(result),
|
||||||
|
'text': self.get_result_label(result),
|
||||||
|
'selected_text': self.get_selected_result_label(result),
|
||||||
|
} for result in results]
|
||||||
|
} for group, results in groups.items()]
|
||||||
|
} for super_group, groups in super_groups.items()]
|
||||||
|
|
||||||
|
|
||||||
|
class BiotopeExtraCodeAutocomplete(KonovaCodeAutocomplete):
|
||||||
|
"""
|
||||||
|
Due to limitations of the django dal package, we need to subclass for each code list
|
||||||
|
"""
|
||||||
|
group_by_related = "parent"
|
||||||
|
related_field_name = "long_name"
|
||||||
|
paginate_by = 200
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.c = CODELIST_BIOTOPES_EXTRA_CODES_ID
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def order_by(self, qs):
|
||||||
|
""" Orders by a predefined value
|
||||||
|
|
||||||
|
Wrapped in a function to provide inheritance-based different orders
|
||||||
|
|
||||||
|
Args:
|
||||||
|
qs (QuerySet): The queryset to be ordered
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
qs (QuerySet): The ordered queryset
|
||||||
|
"""
|
||||||
|
return qs.order_by(
|
||||||
|
"long_name",
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_result_label(self, result):
|
||||||
|
return f"{result.long_name} ({result.short_name})"
|
||||||
|
|
||||||
|
|
||||||
|
class LawCodeAutocomplete(KonovaCodeAutocomplete):
|
||||||
|
"""
|
||||||
|
Due to limitations of the django dal package, we need to subclass for each code list
|
||||||
|
"""
|
||||||
|
group_by_related = "parent"
|
||||||
|
related_field_name = "long_name"
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.c = CODELIST_LAW_ID
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_result_label(self, result):
|
||||||
|
return f"{result.long_name} ({result.short_name})"
|
||||||
|
|
||||||
|
|
||||||
|
class ProcessTypeCodeAutocomplete(KonovaCodeAutocomplete):
|
||||||
|
"""
|
||||||
|
Due to limitations of the django dal package, we need to subclass for each code list
|
||||||
|
"""
|
||||||
|
group_by_related = "parent"
|
||||||
|
related_field_name = "long_name"
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.c = CODELIST_PROCESS_TYPE_ID
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class RegistrationOfficeCodeAutocomplete(KonovaCodeAutocomplete):
|
||||||
|
"""
|
||||||
|
Due to limitations of the django dal package, we need to subclass for each code list
|
||||||
|
"""
|
||||||
|
group_by_related = "parent"
|
||||||
|
related_field_name = "long_name"
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.c = CODELIST_REGISTRATION_OFFICE_ID
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def order_by(self, qs):
|
||||||
|
return qs.order_by(
|
||||||
|
"parent__long_name"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ConservationOfficeCodeAutocomplete(KonovaCodeAutocomplete):
|
||||||
|
"""
|
||||||
|
Due to limitations of the django dal package, we need to subclass for each code list
|
||||||
|
"""
|
||||||
|
group_by_related = "parent"
|
||||||
|
related_field_name = "long_name"
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.c = CODELIST_CONSERVATION_OFFICE_ID
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_result_label(self, result):
|
||||||
|
return f"{result.long_name} ({result.short_name})"
|
||||||
|
|
||||||
|
|
||||||
|
class HandlerCodeAutocomplete(KonovaCodeAutocomplete):
|
||||||
|
"""
|
||||||
|
Due to limitations of the django dal package, we need to subclass for each code list
|
||||||
|
"""
|
||||||
|
group_by_related = "parent"
|
||||||
|
related_field_name = "long_name"
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.c = CODELIST_HANDLER_ID
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_result_label(self, result):
|
||||||
|
return result.long_name
|
414
konova/filters/mixins.py
Normal file
414
konova/filters/mixins.py
Normal file
@ -0,0 +1,414 @@
|
|||||||
|
"""
|
||||||
|
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, filter_q) -> QuerySet:
|
||||||
|
""" Filters the parcel entries by a given filter_q
|
||||||
|
|
||||||
|
Args:
|
||||||
|
queryset (QuerySet): The queryset
|
||||||
|
filter_q (Q): The Q-style filter expression
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
matching_parcels = Parcel.objects.filter(
|
||||||
|
filter_q
|
||||||
|
)
|
||||||
|
|
||||||
|
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(
|
||||||
|
Q(name__icontains=value) |
|
||||||
|
Q(key__icontains=value)
|
||||||
|
).distinct()
|
||||||
|
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,
|
||||||
|
Q(parcel_group__name__icontains=value) | Q(parcel_group__key__icontains=value),
|
||||||
|
)
|
||||||
|
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,
|
||||||
|
Q(flr=value),
|
||||||
|
)
|
||||||
|
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,
|
||||||
|
Q(flrstck_zhlr=value)
|
||||||
|
)
|
||||||
|
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,
|
||||||
|
Q(flrstck_nnr=value),
|
||||||
|
)
|
||||||
|
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(
|
||||||
|
Q(users__in=[self.user]) | # requesting user has access
|
||||||
|
Q(teams__in=self.user.shared_teams)
|
||||||
|
).distinct()
|
||||||
|
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
|
@ -1,7 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
||||||
Created on: 18.08.22
|
|
||||||
|
|
||||||
"""
|
|
@ -1,33 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
||||||
Created on: 18.08.22
|
|
||||||
|
|
||||||
"""
|
|
||||||
from django import forms
|
|
||||||
from django.db.models import QuerySet, Q
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
import django_filters
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
@ -1,209 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
||||||
Created on: 18.08.22
|
|
||||||
|
|
||||||
"""
|
|
||||||
from django import forms
|
|
||||||
from django.db.models import QuerySet, Q
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
import django_filters
|
|
||||||
|
|
||||||
from konova.models import Parcel, District
|
|
||||||
|
|
||||||
|
|
||||||
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, filter_q) -> QuerySet:
|
|
||||||
""" Filters the parcel entries by a given filter_q
|
|
||||||
|
|
||||||
Args:
|
|
||||||
queryset (QuerySet): The queryset
|
|
||||||
filter_q (Q): The Q-style filter expression
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
matching_parcels = Parcel.objects.filter(
|
|
||||||
filter_q
|
|
||||||
)
|
|
||||||
|
|
||||||
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(
|
|
||||||
Q(name__icontains=value) |
|
|
||||||
Q(key__icontains=value)
|
|
||||||
).distinct()
|
|
||||||
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,
|
|
||||||
Q(parcel_group__name__icontains=value) | Q(parcel_group__key__icontains=value),
|
|
||||||
)
|
|
||||||
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,
|
|
||||||
Q(flr=value),
|
|
||||||
)
|
|
||||||
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,
|
|
||||||
Q(flrstck_zhlr=value)
|
|
||||||
)
|
|
||||||
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,
|
|
||||||
Q(flrstck_nnr=value),
|
|
||||||
)
|
|
||||||
return queryset
|
|
@ -1,44 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
||||||
Created on: 18.08.22
|
|
||||||
|
|
||||||
"""
|
|
||||||
import django_filters
|
|
||||||
from django.db.models import QuerySet, Q
|
|
||||||
|
|
||||||
from intervention.inputs import DummyFilterInput
|
|
||||||
|
|
||||||
|
|
||||||
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()
|
|
||||||
value = value.split(" ")
|
|
||||||
# build filter expression
|
|
||||||
q = Q()
|
|
||||||
for val in value:
|
|
||||||
q &= Q(title__icontains=val) | Q(identifier__icontains=val)
|
|
||||||
return queryset.filter(q)
|
|
@ -1,77 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
||||||
Created on: 18.08.22
|
|
||||||
|
|
||||||
"""
|
|
||||||
from dal_select2.widgets import ModelSelect2
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
import django_filters
|
|
||||||
|
|
||||||
from codelist.models import KonovaCode
|
|
||||||
from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID, CODELIST_REGISTRATION_OFFICE_ID
|
|
||||||
|
|
||||||
|
|
||||||
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="codelist: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
|
|
||||||
|
|
||||||
|
|
||||||
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="codelist: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
|
|
@ -1,51 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
||||||
Created on: 18.08.22
|
|
||||||
|
|
||||||
"""
|
|
||||||
from django import forms
|
|
||||||
from django.db.models import QuerySet
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
import django_filters
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
||||||
Created on: 18.08.22
|
|
||||||
|
|
||||||
"""
|
|
||||||
from django import forms
|
|
||||||
from django.db.models import Q, QuerySet
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
import django_filters
|
|
||||||
|
|
||||||
|
|
||||||
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(
|
|
||||||
Q(users__in=[self.user]) | # requesting user has access
|
|
||||||
Q(teams__in=self.user.shared_teams)
|
|
||||||
).distinct()
|
|
||||||
else:
|
|
||||||
return queryset
|
|
@ -7,12 +7,9 @@ Created on: 12.01.22
|
|||||||
"""
|
"""
|
||||||
import django_filters
|
import django_filters
|
||||||
|
|
||||||
from konova.filters.mixins.file_number import FileNumberTableFilterMixin
|
from konova.filters.mixins import RegistrationOfficeTableFilterMixin, ConservationOfficeTableFilterMixin, \
|
||||||
from konova.filters.mixins.geo_reference import GeoReferencedTableFilterMixin
|
KeywordTableFilterMixin, FileNumberTableFilterMixin, GeoReferencedTableFilterMixin, ShareableTableFilterMixin, \
|
||||||
from konova.filters.mixins.keyword import KeywordTableFilterMixin
|
RecordableTableFilterMixin
|
||||||
from konova.filters.mixins.office import ConservationOfficeTableFilterMixin, RegistrationOfficeTableFilterMixin
|
|
||||||
from konova.filters.mixins.record import RecordableTableFilterMixin
|
|
||||||
from konova.filters.mixins.share import ShareableTableFilterMixin
|
|
||||||
|
|
||||||
|
|
||||||
class AbstractTableFilter(django_filters.FilterSet):
|
class AbstractTableFilter(django_filters.FilterSet):
|
||||||
|
@ -21,7 +21,7 @@ class AutocompleteTestCase(BaseTestCase):
|
|||||||
|
|
||||||
def test_user_autocomplete(self):
|
def test_user_autocomplete(self):
|
||||||
self.client.login(username=self.superuser.username, password=self.superuser_pw)
|
self.client.login(username=self.superuser.username, password=self.superuser_pw)
|
||||||
user_autocomplete_url = reverse("user:share-user-autocomplete")
|
user_autocomplete_url = reverse("share-user-autocomplete")
|
||||||
username = self.user.username
|
username = self.user.username
|
||||||
|
|
||||||
# Provide the full name --> success
|
# Provide the full name --> success
|
||||||
@ -60,19 +60,19 @@ class AutocompleteTestCase(BaseTestCase):
|
|||||||
|
|
||||||
def test_all_autocompletes(self):
|
def test_all_autocompletes(self):
|
||||||
tests = [
|
tests = [
|
||||||
"compensation:acc:autocomplete",
|
"accounts-autocomplete",
|
||||||
"intervention:autocomplete",
|
"interventions-autocomplete",
|
||||||
"codelist:compensation-action-autocomplete",
|
"codes-compensation-action-autocomplete",
|
||||||
"codelist:compensation-action-detail-autocomplete",
|
"codes-compensation-action-detail-autocomplete",
|
||||||
"codelist:biotope-autocomplete",
|
"codes-biotope-autocomplete",
|
||||||
"codelist:biotope-extra-type-autocomplete",
|
"codes-biotope-extra-type-autocomplete",
|
||||||
"codelist:law-autocomplete",
|
"codes-law-autocomplete",
|
||||||
"codelist:process-type-autocomplete",
|
"codes-process-type-autocomplete",
|
||||||
"codelist:registration-office-autocomplete",
|
"codes-registration-office-autocomplete",
|
||||||
"codelist:conservation-office-autocomplete",
|
"codes-conservation-office-autocomplete",
|
||||||
"user:share-user-autocomplete",
|
"share-user-autocomplete",
|
||||||
"user:share-team-autocomplete",
|
"share-team-autocomplete",
|
||||||
"user:team-admin-autocomplete",
|
"team-admin-autocomplete",
|
||||||
]
|
]
|
||||||
for test in tests:
|
for test in tests:
|
||||||
self.client.login(username=self.superuser.username, password=self.superuser_pw)
|
self.client.login(username=self.superuser.username, password=self.superuser_pw)
|
||||||
|
@ -590,15 +590,15 @@ class AutocompleteTestCase(BaseViewTestCase):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls) -> None:
|
def setUpTestData(cls) -> None:
|
||||||
super().setUpTestData()
|
super().setUpTestData()
|
||||||
cls.atcmplt_accs = reverse("compensation:acc:autocomplete")
|
cls.atcmplt_accs = reverse("accounts-autocomplete")
|
||||||
cls.atcmplt_interventions = reverse("intervention:autocomplete")
|
cls.atcmplt_interventions = reverse("interventions-autocomplete")
|
||||||
cls.atcmplt_code_comp_action = reverse("codelist:compensation-action-autocomplete")
|
cls.atcmplt_code_comp_action = reverse("codes-compensation-action-autocomplete")
|
||||||
cls.atcmplt_code_comp_biotope = reverse("codelist:biotope-autocomplete")
|
cls.atcmplt_code_comp_biotope = reverse("codes-biotope-autocomplete")
|
||||||
cls.atcmplt_code_comp_law = reverse("codelist:law-autocomplete")
|
cls.atcmplt_code_comp_law = reverse("codes-law-autocomplete")
|
||||||
cls.atcmplt_code_comp_process = reverse("codelist:process-type-autocomplete")
|
cls.atcmplt_code_comp_process = reverse("codes-process-type-autocomplete")
|
||||||
cls.atcmplt_code_comp_reg_off = reverse("codelist:registration-office-autocomplete")
|
cls.atcmplt_code_comp_reg_off = reverse("codes-registration-office-autocomplete")
|
||||||
cls.atcmplt_code_comp_cons_off = reverse("codelist:conservation-office-autocomplete")
|
cls.atcmplt_code_comp_cons_off = reverse("codes-conservation-office-autocomplete")
|
||||||
cls.atcmplt_code_share_user = reverse("user:share-user-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
|
||||||
|
@ -17,6 +17,11 @@ import debug_toolbar
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
|
|
||||||
|
from konova.autocompletes import EcoAccountAutocomplete, \
|
||||||
|
InterventionAutocomplete, CompensationActionCodeAutocomplete, BiotopeCodeAutocomplete, LawCodeAutocomplete, \
|
||||||
|
RegistrationOfficeCodeAutocomplete, ConservationOfficeCodeAutocomplete, ProcessTypeCodeAutocomplete, \
|
||||||
|
ShareUserAutocomplete, BiotopeExtraCodeAutocomplete, CompensationActionDetailCodeAutocomplete, \
|
||||||
|
ShareTeamAutocomplete, HandlerCodeAutocomplete, TeamAdminAutocomplete
|
||||||
from konova.settings import SSO_SERVER, SSO_PUBLIC_KEY, SSO_PRIVATE_KEY, DEBUG
|
from konova.settings import SSO_SERVER, SSO_PUBLIC_KEY, SSO_PRIVATE_KEY, DEBUG
|
||||||
from konova.sso.sso import KonovaSSOClient
|
from konova.sso.sso import KonovaSSOClient
|
||||||
from konova.views import logout_view, home_view, get_geom_parcels, get_geom_parcels_content, map_client_proxy_view
|
from konova.views import logout_view, home_view, get_geom_parcels, get_geom_parcels_content, map_client_proxy_view
|
||||||
@ -38,6 +43,22 @@ urlpatterns = [
|
|||||||
path('geom/<id>/parcels/', get_geom_parcels, name="geometry-parcels"),
|
path('geom/<id>/parcels/', get_geom_parcels, name="geometry-parcels"),
|
||||||
path('geom/<id>/parcels/<int:page>', get_geom_parcels_content, name="geometry-parcels-content"),
|
path('geom/<id>/parcels/<int:page>', get_geom_parcels_content, name="geometry-parcels-content"),
|
||||||
path('client/proxy', map_client_proxy_view, name="map-client-proxy"),
|
path('client/proxy', map_client_proxy_view, name="map-client-proxy"),
|
||||||
|
|
||||||
|
# Autocomplete paths for all apps
|
||||||
|
path("atcmplt/eco-accounts", EcoAccountAutocomplete.as_view(), name="accounts-autocomplete"),
|
||||||
|
path("atcmplt/interventions", InterventionAutocomplete.as_view(), name="interventions-autocomplete"),
|
||||||
|
path("atcmplt/codes/comp/action", CompensationActionCodeAutocomplete.as_view(), name="codes-compensation-action-autocomplete"),
|
||||||
|
path("atcmplt/codes/comp/action/detail", CompensationActionDetailCodeAutocomplete.as_view(), name="codes-compensation-action-detail-autocomplete"),
|
||||||
|
path("atcmplt/codes/biotope", BiotopeCodeAutocomplete.as_view(), name="codes-biotope-autocomplete"),
|
||||||
|
path("atcmplt/codes/biotope/extra", BiotopeExtraCodeAutocomplete.as_view(), name="codes-biotope-extra-type-autocomplete"),
|
||||||
|
path("atcmplt/codes/law", LawCodeAutocomplete.as_view(), name="codes-law-autocomplete"),
|
||||||
|
path("atcmplt/codes/prc-type", ProcessTypeCodeAutocomplete.as_view(), name="codes-process-type-autocomplete"),
|
||||||
|
path("atcmplt/codes/reg-off", RegistrationOfficeCodeAutocomplete.as_view(), name="codes-registration-office-autocomplete"),
|
||||||
|
path("atcmplt/codes/cons-off", ConservationOfficeCodeAutocomplete.as_view(), name="codes-conservation-office-autocomplete"),
|
||||||
|
path("atcmplt/codes/handler", HandlerCodeAutocomplete.as_view(), name="codes-handler-autocomplete"),
|
||||||
|
path("atcmplt/share/u", ShareUserAutocomplete.as_view(), name="share-user-autocomplete"),
|
||||||
|
path("atcmplt/share/t", ShareTeamAutocomplete.as_view(), name="share-team-autocomplete"),
|
||||||
|
path("atcmplt/team/admin", TeamAdminAutocomplete.as_view(), name="team-admin-autocomplete"),
|
||||||
]
|
]
|
||||||
|
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
||||||
Created on: 18.08.22
|
|
||||||
|
|
||||||
"""
|
|
@ -1,52 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
||||||
Created on: 18.08.22
|
|
||||||
|
|
||||||
"""
|
|
||||||
from dal_select2.views import Select2QuerySetView
|
|
||||||
from django.db.models import Q
|
|
||||||
|
|
||||||
from user.models import User, Team
|
|
||||||
|
|
||||||
|
|
||||||
class ShareUserAutocomplete(Select2QuerySetView):
|
|
||||||
""" Autocomplete for share with single users
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
def get_queryset(self):
|
|
||||||
if self.request.user.is_anonymous:
|
|
||||||
return User.objects.none()
|
|
||||||
qs = User.objects.all()
|
|
||||||
if self.q:
|
|
||||||
# Due to privacy concerns only a full username match will return the proper user entry
|
|
||||||
qs = qs.filter(
|
|
||||||
Q(username=self.q) |
|
|
||||||
Q(email=self.q)
|
|
||||||
).distinct()
|
|
||||||
qs = qs.order_by("username")
|
|
||||||
return qs
|
|
||||||
|
|
||||||
|
|
||||||
class ShareTeamAutocomplete(Select2QuerySetView):
|
|
||||||
""" Autocomplete for share with teams
|
|
||||||
|
|
||||||
"""
|
|
||||||
def get_queryset(self):
|
|
||||||
if self.request.user.is_anonymous:
|
|
||||||
return Team.objects.none()
|
|
||||||
qs = Team.objects.filter(
|
|
||||||
deleted__isnull=True
|
|
||||||
)
|
|
||||||
if self.q:
|
|
||||||
# Due to privacy concerns only a full username match will return the proper user entry
|
|
||||||
qs = qs.filter(
|
|
||||||
name__icontains=self.q
|
|
||||||
)
|
|
||||||
qs = qs.order_by(
|
|
||||||
"name"
|
|
||||||
)
|
|
||||||
return qs
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
||||||
Created on: 18.08.22
|
|
||||||
|
|
||||||
"""
|
|
||||||
from dal_select2.views import Select2QuerySetView
|
|
||||||
|
|
||||||
from user.models import User
|
|
||||||
|
|
||||||
|
|
||||||
class TeamAdminAutocomplete(Select2QuerySetView):
|
|
||||||
""" Autocomplete for share with teams
|
|
||||||
|
|
||||||
"""
|
|
||||||
def get_queryset(self):
|
|
||||||
if self.request.user.is_anonymous:
|
|
||||||
return User.objects.none()
|
|
||||||
qs = User.objects.filter(
|
|
||||||
id__in=self.forwarded.get("members", [])
|
|
||||||
).exclude(
|
|
||||||
id__in=self.forwarded.get("admins", [])
|
|
||||||
)
|
|
||||||
if self.q:
|
|
||||||
# Due to privacy concerns only a full username match will return the proper user entry
|
|
||||||
qs = qs.filter(
|
|
||||||
name__icontains=self.q
|
|
||||||
)
|
|
||||||
qs = qs.order_by(
|
|
||||||
"username"
|
|
||||||
)
|
|
||||||
return qs
|
|
||||||
|
|
@ -1,17 +1,166 @@
|
|||||||
"""
|
"""
|
||||||
Author: Michel Peltriaux
|
Author: Michel Peltriaux
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||||
Created on: 18.08.22
|
Created on: 08.07.21
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from dal import autocomplete
|
from dal import autocomplete
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.urls import reverse
|
from django.urls import reverse, reverse_lazy
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from api.models import APIUserToken
|
||||||
|
from intervention.inputs import GenerateInput
|
||||||
|
from user.models import User, UserNotification, Team
|
||||||
|
|
||||||
from konova.forms.modals import BaseModalForm, RemoveModalForm
|
from konova.forms.modals import BaseModalForm, RemoveModalForm
|
||||||
from user.models import User, Team
|
from konova.forms import BaseForm
|
||||||
|
|
||||||
|
|
||||||
|
class UserNotificationForm(BaseForm):
|
||||||
|
""" Form for changing the notification settings of a user
|
||||||
|
|
||||||
|
"""
|
||||||
|
notifications = forms.MultipleChoiceField(
|
||||||
|
label_suffix="",
|
||||||
|
label=_("Notifications"),
|
||||||
|
required=False, # allow total disabling of all notifications
|
||||||
|
help_text=_("Select the situations when you want to receive a notification"),
|
||||||
|
widget=forms.CheckboxSelectMultiple(
|
||||||
|
attrs={
|
||||||
|
"class": "list-unstyled",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
choices=[]
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, user: User, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.user = user
|
||||||
|
self.form_title = _("Edit notifications")
|
||||||
|
self.form_caption = _("")
|
||||||
|
self.action_url = reverse("user:notifications")
|
||||||
|
self.cancel_redirect = reverse("user:index")
|
||||||
|
|
||||||
|
# Insert all notifications into form field by creating choices as tuples
|
||||||
|
notifications = UserNotification.objects.filter(
|
||||||
|
is_active=True,
|
||||||
|
)
|
||||||
|
choices = []
|
||||||
|
for n in notifications:
|
||||||
|
choices.append(
|
||||||
|
(n.id, _(n.name))
|
||||||
|
)
|
||||||
|
self.fields["notifications"].choices = choices
|
||||||
|
|
||||||
|
users_current_notifications = self.user.notifications.all()
|
||||||
|
users_current_notifications = [str(n.id) for n in users_current_notifications]
|
||||||
|
self.fields["notifications"].initial = users_current_notifications
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
""" Stores the changes in the user konova_extension
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
selected_notification_ids = self.cleaned_data.get("notifications", [])
|
||||||
|
notifications = UserNotification.objects.filter(
|
||||||
|
id__in=selected_notification_ids,
|
||||||
|
)
|
||||||
|
self.user.notifications.set(notifications)
|
||||||
|
|
||||||
|
|
||||||
|
class UserContactForm(BaseModalForm):
|
||||||
|
name = forms.CharField(
|
||||||
|
label=_("Username"),
|
||||||
|
label_suffix="",
|
||||||
|
required=False,
|
||||||
|
widget=forms.TextInput(
|
||||||
|
attrs={
|
||||||
|
"readonly": True,
|
||||||
|
"class": "form-control",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
person_name = forms.CharField(
|
||||||
|
label=_("Person name"),
|
||||||
|
label_suffix="",
|
||||||
|
required=False,
|
||||||
|
widget=forms.TextInput(
|
||||||
|
attrs={
|
||||||
|
"readonly": True,
|
||||||
|
"class": "form-control",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
mail = forms.EmailField(
|
||||||
|
label=_("E-Mail"),
|
||||||
|
label_suffix="",
|
||||||
|
required=False,
|
||||||
|
widget=forms.TextInput(
|
||||||
|
attrs={
|
||||||
|
"readonly": True,
|
||||||
|
"class": "form-control",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.render_submit = False
|
||||||
|
self.form_title = _("User contact data")
|
||||||
|
self.form_caption = ""
|
||||||
|
|
||||||
|
self.initialize_form_field("name", self.instance.username)
|
||||||
|
self.initialize_form_field("person_name", "{} {}".format(self.instance.first_name, self.instance.last_name))
|
||||||
|
self.initialize_form_field("mail", self.instance.email)
|
||||||
|
|
||||||
|
|
||||||
|
class UserAPITokenForm(BaseForm):
|
||||||
|
token = forms.CharField(
|
||||||
|
label=_("Token"),
|
||||||
|
label_suffix="",
|
||||||
|
max_length=255,
|
||||||
|
required=True,
|
||||||
|
help_text=_("Generated automatically"),
|
||||||
|
widget=GenerateInput(
|
||||||
|
attrs={
|
||||||
|
"class": "form-control",
|
||||||
|
"url": reverse_lazy("api:generate-new-token"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.form_title = _("Create new token")
|
||||||
|
self.form_caption = _("A new token needs to be validated by an administrator!")
|
||||||
|
|
||||||
|
self.action_url = reverse("user:api-token")
|
||||||
|
self.cancel_redirect = reverse("user:index")
|
||||||
|
|
||||||
|
# Make direct token editing by user impossible. Instead set the proper url for generating a new token
|
||||||
|
self.initialize_form_field("token", None)
|
||||||
|
self.fields["token"].widget.attrs["readonly"] = True
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
""" Saves the form data
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
api_token (APIUserToken)
|
||||||
|
"""
|
||||||
|
user = self.instance
|
||||||
|
new_token = self.cleaned_data["token"]
|
||||||
|
if user.api_token is not None:
|
||||||
|
user.api_token.delete()
|
||||||
|
new_token = APIUserToken.objects.create(
|
||||||
|
token=new_token
|
||||||
|
)
|
||||||
|
user.api_token = new_token
|
||||||
|
user.save()
|
||||||
|
return new_token
|
||||||
|
|
||||||
|
|
||||||
class NewTeamModalForm(BaseModalForm):
|
class NewTeamModalForm(BaseModalForm):
|
||||||
@ -43,7 +192,7 @@ class NewTeamModalForm(BaseModalForm):
|
|||||||
required=True,
|
required=True,
|
||||||
queryset=User.objects.all(),
|
queryset=User.objects.all(),
|
||||||
widget=autocomplete.ModelSelect2Multiple(
|
widget=autocomplete.ModelSelect2Multiple(
|
||||||
url="user:share-user-autocomplete",
|
url="share-user-autocomplete",
|
||||||
attrs={
|
attrs={
|
||||||
"data-placeholder": _("Click for selection"),
|
"data-placeholder": _("Click for selection"),
|
||||||
"data-minimum-input-length": 3,
|
"data-minimum-input-length": 3,
|
||||||
@ -103,7 +252,7 @@ class EditTeamModalForm(NewTeamModalForm):
|
|||||||
required=True,
|
required=True,
|
||||||
queryset=User.objects.all(),
|
queryset=User.objects.all(),
|
||||||
widget=autocomplete.ModelSelect2Multiple(
|
widget=autocomplete.ModelSelect2Multiple(
|
||||||
url="user:team-admin-autocomplete",
|
url="team-admin-autocomplete",
|
||||||
forward=[
|
forward=[
|
||||||
"members",
|
"members",
|
||||||
"admins",
|
"admins",
|
||||||
@ -198,3 +347,46 @@ class LeaveTeamModalForm(RemoveModalForm):
|
|||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
self.instance.remove_user(self.user)
|
self.instance.remove_user(self.user)
|
||||||
|
|
||||||
|
|
||||||
|
class TeamDataForm(BaseModalForm):
|
||||||
|
name = forms.CharField(
|
||||||
|
label_suffix="",
|
||||||
|
label=_("Team name"),
|
||||||
|
max_length=500,
|
||||||
|
required=False,
|
||||||
|
widget=forms.TextInput(
|
||||||
|
attrs={
|
||||||
|
"placeholder": _("Team name"),
|
||||||
|
"class": "form-control",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
description = forms.CharField(
|
||||||
|
label_suffix="",
|
||||||
|
required=False,
|
||||||
|
label=_("Description"),
|
||||||
|
widget=forms.Textarea(
|
||||||
|
attrs={
|
||||||
|
"rows": 5,
|
||||||
|
"class": "form-control"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.form_title = _("Team")
|
||||||
|
self.form_caption = ""
|
||||||
|
self.render_submit = False
|
||||||
|
form_data = {
|
||||||
|
"name": self.instance.name,
|
||||||
|
"description": self.instance.description,
|
||||||
|
}
|
||||||
|
self.load_initial_data(
|
||||||
|
form_data,
|
||||||
|
[
|
||||||
|
"name",
|
||||||
|
"description"
|
||||||
|
]
|
||||||
|
)
|
@ -1,7 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
||||||
Created on: 18.08.22
|
|
||||||
|
|
||||||
"""
|
|
@ -1,7 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
||||||
Created on: 18.08.22
|
|
||||||
|
|
||||||
"""
|
|
@ -1,62 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
||||||
Created on: 18.08.22
|
|
||||||
|
|
||||||
"""
|
|
||||||
from django import forms
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
|
|
||||||
from konova.forms.modals import BaseModalForm
|
|
||||||
|
|
||||||
|
|
||||||
class UserContactForm(BaseModalForm):
|
|
||||||
def save(self):
|
|
||||||
# Readonly form. No saving needed
|
|
||||||
pass
|
|
||||||
|
|
||||||
name = forms.CharField(
|
|
||||||
label=_("Username"),
|
|
||||||
label_suffix="",
|
|
||||||
required=False,
|
|
||||||
widget=forms.TextInput(
|
|
||||||
attrs={
|
|
||||||
"readonly": True,
|
|
||||||
"class": "form-control",
|
|
||||||
}
|
|
||||||
),
|
|
||||||
)
|
|
||||||
person_name = forms.CharField(
|
|
||||||
label=_("Person name"),
|
|
||||||
label_suffix="",
|
|
||||||
required=False,
|
|
||||||
widget=forms.TextInput(
|
|
||||||
attrs={
|
|
||||||
"readonly": True,
|
|
||||||
"class": "form-control",
|
|
||||||
}
|
|
||||||
),
|
|
||||||
)
|
|
||||||
mail = forms.EmailField(
|
|
||||||
label=_("E-Mail"),
|
|
||||||
label_suffix="",
|
|
||||||
required=False,
|
|
||||||
widget=forms.TextInput(
|
|
||||||
attrs={
|
|
||||||
"readonly": True,
|
|
||||||
"class": "form-control",
|
|
||||||
}
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.render_submit = False
|
|
||||||
self.form_title = _("User contact data")
|
|
||||||
self.form_caption = ""
|
|
||||||
|
|
||||||
self.initialize_form_field("name", self.instance.username)
|
|
||||||
self.initialize_form_field("person_name", "{} {}".format(self.instance.first_name, self.instance.last_name))
|
|
||||||
self.initialize_form_field("mail", self.instance.email)
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
||||||
Created on: 18.08.22
|
|
||||||
|
|
||||||
"""
|
|
||||||
from django import forms
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
|
|
||||||
from konova.forms.modals import BaseModalForm
|
|
||||||
|
|
||||||
|
|
||||||
class TeamDataForm(BaseModalForm):
|
|
||||||
name = forms.CharField(
|
|
||||||
label_suffix="",
|
|
||||||
label=_("Team name"),
|
|
||||||
max_length=500,
|
|
||||||
required=False,
|
|
||||||
widget=forms.TextInput(
|
|
||||||
attrs={
|
|
||||||
"placeholder": _("Team name"),
|
|
||||||
"class": "form-control",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
description = forms.CharField(
|
|
||||||
label_suffix="",
|
|
||||||
required=False,
|
|
||||||
label=_("Description"),
|
|
||||||
widget=forms.Textarea(
|
|
||||||
attrs={
|
|
||||||
"rows": 5,
|
|
||||||
"class": "form-control"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.form_title = _("Team")
|
|
||||||
self.form_caption = ""
|
|
||||||
self.render_submit = False
|
|
||||||
form_data = {
|
|
||||||
"name": self.instance.name,
|
|
||||||
"description": self.instance.description,
|
|
||||||
}
|
|
||||||
self.load_initial_data(
|
|
||||||
form_data,
|
|
||||||
[
|
|
||||||
"name",
|
|
||||||
"description"
|
|
||||||
]
|
|
||||||
)
|
|
@ -1,113 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
||||||
Created on: 18.08.22
|
|
||||||
|
|
||||||
"""
|
|
||||||
from django import forms
|
|
||||||
from django.urls import reverse, reverse_lazy
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
|
|
||||||
from api.models import APIUserToken
|
|
||||||
from intervention.inputs import GenerateInput
|
|
||||||
from konova.forms import BaseForm
|
|
||||||
from user.models import User, UserNotification
|
|
||||||
|
|
||||||
|
|
||||||
class UserNotificationForm(BaseForm):
|
|
||||||
""" Form for changing the notification settings of a user
|
|
||||||
|
|
||||||
"""
|
|
||||||
notifications = forms.MultipleChoiceField(
|
|
||||||
label_suffix="",
|
|
||||||
label=_("Notifications"),
|
|
||||||
required=False, # allow total disabling of all notifications
|
|
||||||
help_text=_("Select the situations when you want to receive a notification"),
|
|
||||||
widget=forms.CheckboxSelectMultiple(
|
|
||||||
attrs={
|
|
||||||
"class": "list-unstyled",
|
|
||||||
}
|
|
||||||
),
|
|
||||||
choices=[]
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, user: User, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.user = user
|
|
||||||
self.form_title = _("Edit notifications")
|
|
||||||
self.form_caption = _("")
|
|
||||||
self.action_url = reverse("user:notifications")
|
|
||||||
self.cancel_redirect = reverse("user:index")
|
|
||||||
|
|
||||||
# Insert all notifications into form field by creating choices as tuples
|
|
||||||
notifications = UserNotification.objects.filter(
|
|
||||||
is_active=True,
|
|
||||||
)
|
|
||||||
choices = []
|
|
||||||
for n in notifications:
|
|
||||||
choices.append(
|
|
||||||
(n.id, _(n.name))
|
|
||||||
)
|
|
||||||
self.fields["notifications"].choices = choices
|
|
||||||
|
|
||||||
users_current_notifications = self.user.notifications.all()
|
|
||||||
users_current_notifications = [str(n.id) for n in users_current_notifications]
|
|
||||||
self.fields["notifications"].initial = users_current_notifications
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
""" Stores the changes in the user konova_extension
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
selected_notification_ids = self.cleaned_data.get("notifications", [])
|
|
||||||
notifications = UserNotification.objects.filter(
|
|
||||||
id__in=selected_notification_ids,
|
|
||||||
)
|
|
||||||
self.user.notifications.set(notifications)
|
|
||||||
|
|
||||||
|
|
||||||
class UserAPITokenForm(BaseForm):
|
|
||||||
token = forms.CharField(
|
|
||||||
label=_("Token"),
|
|
||||||
label_suffix="",
|
|
||||||
max_length=255,
|
|
||||||
required=True,
|
|
||||||
help_text=_("Generated automatically"),
|
|
||||||
widget=GenerateInput(
|
|
||||||
attrs={
|
|
||||||
"class": "form-control",
|
|
||||||
"url": reverse_lazy("api:generate-new-token"),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.form_title = _("Create new token")
|
|
||||||
self.form_caption = _("A new token needs to be validated by an administrator!")
|
|
||||||
|
|
||||||
self.action_url = reverse("user:api-token")
|
|
||||||
self.cancel_redirect = reverse("user:index")
|
|
||||||
|
|
||||||
# Make direct token editing by user impossible. Instead set the proper url for generating a new token
|
|
||||||
self.initialize_form_field("token", None)
|
|
||||||
self.fields["token"].widget.attrs["readonly"] = True
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
""" Saves the form data
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
api_token (APIUserToken)
|
|
||||||
"""
|
|
||||||
user = self.instance
|
|
||||||
new_token = self.cleaned_data["token"]
|
|
||||||
if user.api_token is not None:
|
|
||||||
user.api_token.delete()
|
|
||||||
new_token = APIUserToken.objects.create(
|
|
||||||
token=new_token
|
|
||||||
)
|
|
||||||
user.api_token = new_token
|
|
||||||
user.save()
|
|
||||||
return new_token
|
|
@ -7,8 +7,6 @@ Created on: 08.07.21
|
|||||||
"""
|
"""
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
from user.autocomplete.share import ShareUserAutocomplete, ShareTeamAutocomplete
|
|
||||||
from user.autocomplete.team import TeamAdminAutocomplete
|
|
||||||
from user.views import *
|
from user.views import *
|
||||||
|
|
||||||
app_name = "user"
|
app_name = "user"
|
||||||
@ -24,8 +22,4 @@ urlpatterns = [
|
|||||||
path("team/<id>/remove", remove_team_view, name="team-remove"),
|
path("team/<id>/remove", remove_team_view, name="team-remove"),
|
||||||
path("team/<id>/leave", leave_team_view, name="team-leave"),
|
path("team/<id>/leave", leave_team_view, name="team-leave"),
|
||||||
|
|
||||||
# Autocomplete urls
|
|
||||||
path("atcmplt/share/u", ShareUserAutocomplete.as_view(), name="share-user-autocomplete"),
|
|
||||||
path("atcmplt/share/t", ShareTeamAutocomplete.as_view(), name="share-team-autocomplete"),
|
|
||||||
path("atcmplt/team/admin", TeamAdminAutocomplete.as_view(), name="team-admin-autocomplete"),
|
|
||||||
]
|
]
|
@ -5,10 +5,6 @@ from django.urls import reverse
|
|||||||
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||||
from konova.utils.mailer import Mailer
|
from konova.utils.mailer import Mailer
|
||||||
from konova.utils.message_templates import FORM_INVALID
|
from konova.utils.message_templates import FORM_INVALID
|
||||||
from user.forms.modals.team import NewTeamModalForm, EditTeamModalForm, RemoveTeamModalForm, LeaveTeamModalForm
|
|
||||||
from user.forms.modals.user import UserContactForm
|
|
||||||
from user.forms.team import TeamDataForm
|
|
||||||
from user.forms.user import UserNotificationForm, UserAPITokenForm
|
|
||||||
from user.models import User, Team
|
from user.models import User, Team
|
||||||
from django.http import HttpRequest, Http404
|
from django.http import HttpRequest, Http404
|
||||||
from django.shortcuts import render, redirect, get_object_or_404
|
from django.shortcuts import render, redirect, get_object_or_404
|
||||||
@ -16,6 +12,8 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
|
|
||||||
from konova.contexts import BaseContext
|
from konova.contexts import BaseContext
|
||||||
from konova.decorators import any_group_check, default_group_required
|
from konova.decorators import any_group_check, default_group_required
|
||||||
|
from user.forms import UserNotificationForm, UserContactForm, UserAPITokenForm, NewTeamModalForm, EditTeamModalForm, \
|
||||||
|
RemoveTeamModalForm, TeamDataForm, LeaveTeamModalForm
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
Loading…
Reference in New Issue
Block a user