Compare commits

...

30 Commits

Author SHA1 Message Date
a2d5c4ddaa Merge remote-tracking branch 'origin/Docker' into Docker
# Conflicts:
#	docker-compose.yml
2022-01-13 14:07:54 +01:00
2118b42ecb # Docker persistent storage
* adds named volume to docker-compose.yml to keep files uploaded on /konova_uploaded_files even on image rebuild
2022-01-13 14:07:18 +01:00
0c2d39ba96 # Docker
* minor adjustments
2022-01-13 09:29:38 +01:00
9cb9308f85 # Docker Production
* adds further settings to create a production-ready docker configuration
2022-01-13 09:29:38 +01:00
d9577fe6a1 # Docker
* adds docker related configurations
* directly working configuration provided
2022-01-13 09:29:38 +01:00
1fc0fbbf0a Merge pull request '#67 Parcel error fix' (#69) from 67_Parcel_fetcher_error into master
Reviewed-on: SGD-Nord/konova#69
2022-01-13 09:28:14 +01:00
022d9d15a7 #67 Parcel error fix
* adds workaround in case of received WFS exception -> routine waits 1 second and tries to rerun for another time. If this does fail as well, the routine will end without a result
* adds mailto shortcut to contact button in footer for a quick fix
2022-01-13 09:27:35 +01:00
be7934f34a Merge pull request '#53 Remove buttons' (#68) from 53_Remove_Import-Export into master
Reviewed-on: SGD-Nord/konova#68
2022-01-13 09:00:05 +01:00
b34853089e #53 Remove buttons
* removes (non-functional) buttons in frontend
2022-01-13 08:59:36 +01:00
d08260a519 Merge pull request '# 62 404 and 500' (#66) from 62_custom_404_500 into master
Reviewed-on: SGD-Nord/konova#66
2022-01-12 16:46:52 +01:00
6f76b3a53d # 62 404 and 500
* adds custom 404 and 500 error handling views
* adds/updates translations
2022-01-12 16:45:55 +01:00
86aafcc5d4 Merge pull request '63_Email' (#65) from 63_Email into master
Reviewed-on: SGD-Nord/konova#65
2022-01-12 16:04:42 +01:00
795f64f20b # 63 Mail data checked
* adds mail sending if shared data is checked
* adds/updates translations
2022-01-12 15:48:47 +01:00
5ca4c7c777 # 63 UserNotificationEnum changes
* drops NOTIFY_ON_REGISTERED_DATA_EDITED in favor of NOTIFY_ON_SHARED_DATA_CHECKED
* adds/updates translations
2022-01-12 15:38:57 +01:00
7c5454a2c9 # 63 Mail data deleted
* adds mail sending if shared data is deleted
* adds/updates translations
* refactors recording mails into separate email template folder email/recording
2022-01-12 15:31:25 +01:00
157f05ead6 # 63 Mail recording/unrecording
* adds mail sending in case of unrecording and recording of data
* adds/updates translations
2022-01-12 15:09:52 +01:00
17dc3f7537 # 63 Mail celery
* moves mail sending to celery worker using shared_task in konova/tasks.py
* adds mail sending for shared access given
* adds UserNotification settings checks for mail sending
* adds/updates translations
2022-01-12 14:51:50 +01:00
dbf32f3138 # 63 Mail background sending
* moves mail sending to background using celery
2022-01-12 14:27:39 +01:00
9c4bab3800 # 63 Mail shared access revoked
* adds mail support for revoking shared access
* adds/updates translations
2022-01-12 14:17:12 +01:00
02970b19b4 # 63 Refactoring
* refactors django User model to custom User model to provide further attributes and methods directly on the user model
2022-01-12 12:56:22 +01:00
37fffd639f Merge pull request '61_Extend_filter' (#64) from 61_Extend_filter into master
Reviewed-on: SGD-Nord/konova#64
2022-01-12 10:20:19 +01:00
96f2e77972 # 61 EcoAccount Filter
* adds specialized SelectionTableFilter for EcoAccount (and EMA) which only provides filtering by conservation office
2022-01-12 10:15:57 +01:00
4c5e170b85 # 61 General table enhancements
* enhances rendering of tables
* enhances rendering of filter section
* reorganizes table filter codes into konova/filters/ folder and splits into mixins and table_filters
2022-01-12 10:11:47 +01:00
d57478ee09 # 61 Filter code enhancement
* reorganizes code
2022-01-12 09:23:22 +01:00
834fa8dbd4 # 61 Filter offices frontend/backend
* adds filters for registration and conservation offices
* adds SelectionTableFilter as new holder for these types of filter components
2022-01-12 08:56:49 +01:00
5bca8fc8f7 # 61 Filter frontend
* adds filtering for file number
* updates translations
2022-01-11 17:29:36 +01:00
5d5d8b1243 # 61 Filter frontend
* finished filter frontend
2022-01-11 16:32:33 +01:00
8a19d7239d # 61 Filter backend
* refactors XYTableFilter classes to hold query_filter and checkbox_filter which are separate instances of django_filter.FilterSets
* improves render ability for frontend
2022-01-11 15:19:16 +01:00
87f11a9914 # 61 Filter frontend
* optimizes minor parts of table-filter initialization and queryset passing
2022-01-11 14:28:34 +01:00
524b5c340f # 61 Filter backend
* refactors table filters by introducing AbstractTableFilter, GeoReferencedTableFilterMixin, ShareableTableFilterMixin and RecordableTableFilterMixin
* adds full filter functionality
* refactors TableFilter inheritances for all objects
2022-01-11 12:56:27 +01:00
60 changed files with 1699 additions and 397 deletions

View File

@ -8,20 +8,45 @@ Created on: 29.07.21
import django_filters
from django.utils.translation import gettext_lazy as _
from django import forms
from django.db.models import QuerySet
from django.db.models import QuerySet, Q
from intervention.filters import InterventionTableFilter
from konova.filters.mixins import ConservationOfficeTableFilterMixin
from konova.filters.table_filters import QueryTableFilter, CheckboxTableFilter, SelectionTableFilter, AbstractTableFilter
class CompensationTableFilter(InterventionTableFilter):
""" TableFilter for compensations
class SelectionCompensationTableFilter(SelectionTableFilter):
""" Specialization of regular SelectionTableFilter for compensation model
Based widely on InterventionTableFilter.
Just some minor changes for Compensation model.
"""
def filter_reg_office(self, queryset, name, value):
return queryset.filter(
intervention__responsible__registration_office=value
)
def filter_cons_office(self, queryset, name, value):
return queryset.filter(
intervention__responsible__conservation_office=value
)
class QueryCompensationTableFilter(QueryTableFilter):
""" Specialization of regular QueryTableFilter for compensation model
"""
def filter_file_number(self, queryset, name, value) -> QuerySet:
queryset = queryset.filter(
Q(intervention__responsible__registration_file_number__icontains=value) |
Q(intervention__responsible__conservation_file_number__icontains=value)
)
return queryset
class CheckboxCompensationTableFilter(CheckboxTableFilter):
""" Specialization of regular CheckboxTableFilter for compensation model
"""
def _filter_show_all(self, queryset, name, value) -> QuerySet:
def filter_show_all(self, queryset, name, value) -> QuerySet:
""" Filters queryset depending on value of 'show_all' setting
Args:
@ -39,7 +64,7 @@ class CompensationTableFilter(InterventionTableFilter):
else:
return queryset
def _filter_show_recorded(self, queryset, name, value) -> QuerySet:
def filter_show_recorded(self, queryset, name, value) -> QuerySet:
""" Filters queryset depending on value of 'show_recorded' setting
Args:
@ -58,21 +83,51 @@ class CompensationTableFilter(InterventionTableFilter):
return queryset
class EcoAccountTableFilter(InterventionTableFilter):
""" TableFilter for eco accounts
class CompensationTableFilter(AbstractTableFilter):
""" TableFilter for compensations
Based widely on InterventionTableFilter.
Just some minor changes for EcoAccount model.
Just some minor changes for Compensation model.
"""
def __init__(self, user=None, *args, **kwargs):
super().__init__(*args, **kwargs)
self.user = user
qs = kwargs.get("queryset", None)
request_data = kwargs.get("data", None)
# Overwrite all filters for special needs of compensations
self.selection_filter = SelectionCompensationTableFilter(
data=request_data,
queryset=qs,
)
self.query_filter = QueryCompensationTableFilter(
data=request_data,
queryset=self.selection_filter.qs,
)
self.checkbox_filter = CheckboxCompensationTableFilter(
user=user,
data=request_data,
queryset=self.query_filter.qs,
)
# Overwrite final queryset as well
self.qs = self.checkbox_filter.qs
class CheckboxEcoAccountTableFilter(CheckboxTableFilter):
sr = django_filters.BooleanFilter(
method='_filter_only_show_unrecorded',
method='filter_only_show_unrecorded',
label=_("Show only unrecorded"),
label_suffix=_(""),
widget=forms.CheckboxInput()
widget=forms.CheckboxInput(
attrs={
"class": "form-check-input",
}
)
)
def _filter_show_all(self, queryset, name, value) -> QuerySet:
def filter_show_all(self, queryset, name, value) -> QuerySet:
""" Filters queryset depending on value of 'show_all' setting
Args:
@ -90,7 +145,7 @@ class EcoAccountTableFilter(InterventionTableFilter):
else:
return queryset
def _filter_only_show_unrecorded(self, queryset, name, value) -> QuerySet:
def filter_only_show_unrecorded(self, queryset, name, value) -> QuerySet:
""" Filters queryset depending on value of 'show_recorded' setting
Args:
@ -107,3 +162,40 @@ class EcoAccountTableFilter(InterventionTableFilter):
)
else:
return queryset
class SelectionEcoAccountTableFilter(ConservationOfficeTableFilterMixin):
""" Special selection table filter for eco accounts
EcoAccounts only need a selection filter for conservation office
"""
pass
class EcoAccountTableFilter(AbstractTableFilter):
""" TableFilter for eco accounts
"""
def __init__(self, user=None, *args, **kwargs):
super().__init__(*args, **kwargs)
self.user = user
qs = kwargs.get("queryset", None)
request_data = kwargs.get("data", None)
# Pipe the queryset through all needed filters
self.selection_filter = SelectionEcoAccountTableFilter(
data=request_data,
queryset=qs,
)
self.query_filter = QueryTableFilter(
data=request_data,
queryset=self.selection_filter.qs,
)
self.checkbox_filter = CheckboxEcoAccountTableFilter(
user=user,
data=request_data,
queryset=self.query_filter.qs,
)
# Overwrite the final queryset result
self.qs = self.checkbox_filter.qs

View File

@ -6,7 +6,7 @@ Created on: 04.12.20
"""
from dal import autocomplete
from django.contrib.auth.models import User
from user.models import User
from django.db import transaction
from django.urls import reverse_lazy, reverse
from django.utils.translation import gettext_lazy as _

View File

@ -8,7 +8,7 @@ Created on: 16.11.21
import shutil
from django.contrib import messages
from django.contrib.auth.models import User
from user.models import User
from django.db import models, transaction
from django.db.models import QuerySet, Sum
from django.http import HttpRequest

View File

@ -7,7 +7,7 @@ Created on: 16.11.21
"""
import shutil
from django.contrib.auth.models import User
from user.models import User
from django.core.exceptions import ValidationError
from django.core.validators import MinValueValidator
from django.db import models, transaction

View File

@ -5,7 +5,7 @@ Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 01.12.20
"""
from django.contrib.auth.models import User
from user.models import User
from django.http import HttpRequest
from django.template.loader import render_to_string
from django.urls import reverse
@ -16,11 +16,11 @@ from django.utils.translation import gettext_lazy as _
from compensation.filters import CompensationTableFilter, EcoAccountTableFilter
from compensation.models import Compensation, EcoAccount
from konova.sub_settings.django_settings import DEFAULT_DATE_TIME_FORMAT
from konova.utils.tables import BaseTable
from konova.utils.tables import BaseTable, TableRenderMixin
import django_tables2 as tables
class CompensationTable(BaseTable):
class CompensationTable(BaseTable, TableRenderMixin):
id = tables.Column(
verbose_name=_("Identifier"),
orderable=True,
@ -67,7 +67,8 @@ class CompensationTable(BaseTable):
data=request.GET,
queryset=qs,
)
super().__init__(request, self.filter, *args, **kwargs)
kwargs["queryset"] = self.filter.qs
super().__init__(request, *args, **kwargs)
def render_id(self, value, record: Compensation):
""" Renders the id column for a compensation
@ -161,7 +162,7 @@ class CompensationTable(BaseTable):
return format_html(html)
class EcoAccountTable(BaseTable):
class EcoAccountTable(BaseTable, TableRenderMixin):
id = tables.Column(
verbose_name=_("Identifier"),
orderable=True,
@ -207,7 +208,8 @@ class EcoAccountTable(BaseTable):
data=request.GET,
queryset=qs,
)
super().__init__(request, self.filter, *args, **kwargs)
kwargs["queryset"] = self.filter.qs
super().__init__(request, *args, **kwargs)
def render_id(self, value, record: EcoAccount):
""" Renders the id column for an eco account

View File

@ -58,20 +58,8 @@ class EcoAccountQualityChecker(CompensationQualityChecker):
self._check_deductable_surface()
self._check_responsible_data()
self._check_legal_data()
self._check_record_state()
super().run_check()
def _check_record_state(self):
""" Checks the data quality for recorded state
Returns:
"""
if self.obj.recorded is None:
self.messages.append(
_("Not recorded")
)
def _check_legal_data(self):
""" Checks the data quality for Legal

View File

@ -17,6 +17,7 @@ services:
command: gunicorn konova.wsgi:application --bind 0.0.0.0:8000
volumes:
- .:/konova
- konova_uploaded_files:/konova_uploaded_files
- static_file_volume:/konova/static # Point to the volume for static files. Shared with nginx service
expose:
- 8000
@ -39,6 +40,7 @@ services:
command: celery -A konova worker -l INFO
volumes:
- .:/konova
- konova_uploaded_files:/konova_uploaded_files
depends_on:
- konova
environment:
@ -67,4 +69,5 @@ networks:
name: postgis_nat_it_backend
volumes:
static_file_volume:
static_file_volume:
konova_uploaded_files:

View File

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

View File

@ -7,7 +7,7 @@ Created on: 06.10.21
"""
from dal import autocomplete
from django import forms
from django.contrib.auth.models import User
from user.models import User
from django.db import transaction
from django.urls import reverse, reverse_lazy
from django.utils.translation import gettext_lazy as _

View File

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

View File

@ -5,119 +5,29 @@ Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 22.07.21
"""
import django_filters
from django import forms
from django.contrib.auth.models import User
from django.db.models import QuerySet, Q
from django.utils.translation import gettext_lazy as _
from intervention.inputs import DummyFilterInput
from intervention.models import Intervention
from konova.filters.table_filters import AbstractTableFilter, SelectionTableFilter, QueryTableFilter, CheckboxTableFilter
class InterventionTableFilter(django_filters.FilterSet):
""" TableFilter for Intervention model
"""
sa = django_filters.BooleanFilter(
method='_filter_show_all',
label=_("Show unshared"),
label_suffix=_(""),
widget=forms.CheckboxInput()
)
q = django_filters.Filter(
method='_filter_by_keyword',
# Since we use a custom search bar in the template, we need to 'render' this filter
# as 'anonymous' HiddenInput (no id, no name). This way our custom search bar's id and name won't be
# overwritten with these id and name (which would be equal)
# This way we can use the simple filter method mapping for a parameter without using a predefined widget!
widget=DummyFilterInput(),
)
sr = django_filters.BooleanFilter(
method='_filter_show_recorded',
label=_("Show recorded"),
label_suffix=_(""),
widget=forms.CheckboxInput()
)
# Gemarkung ##ToDo
g = django_filters.CharFilter(
field_name="name",
lookup_expr="icontains",
label=_(""),
label_suffix=_(""),
widget=forms.TextInput(
attrs={
"placeholder": _("District"),
"title": _("Search for district")
}
),
)
# Kreis
## ToDo
# Flur
## ToDo
# Zähler
## ToDo
# Nenner
## ToDo
class Meta:
model = Intervention
fields = []
def __init__(self, user: User, *args, **kwargs):
self.user = user
class InterventionTableFilter(AbstractTableFilter):
def __init__(self, user=None, *args, **kwargs):
super().__init__(*args, **kwargs)
self.user = user
qs = kwargs.get("queryset", None)
request_data = kwargs.get("data", None)
def _filter_by_keyword(self, queryset, name, value) -> QuerySet:
""" Filters queryset depending on value of search bar input
# Pipe the queryset through all needed filters
self.selection_filter = SelectionTableFilter(
data=request_data,
queryset=qs,
)
self.query_filter = QueryTableFilter(
data=request_data,
queryset=self.selection_filter.qs,
)
self.checkbox_filter = CheckboxTableFilter(
user=user,
data=request_data,
queryset=self.query_filter.qs,
)
self.qs = self.checkbox_filter.qs
Args:
queryset ():
name ():
value ():
Returns:
"""
value = value.strip()
# build filter expression
q = Q(title__icontains=value) | Q(identifier__icontains=value)
return queryset.filter(q)
def _filter_show_all(self, queryset, name, value) -> QuerySet:
""" Filters queryset depending on value of 'show_all' setting
Args:
queryset ():
name ():
value ():
Returns:
"""
if not value:
return queryset.filter(
users__in=[self.user], # requesting user has access
)
else:
return queryset
def _filter_show_recorded(self, queryset, name, value) -> QuerySet:
""" Filters queryset depending on value of 'show_recorded' setting
Args:
queryset ():
name ():
value ():
Returns:
"""
if not value:
return queryset.filter(
recorded=None,
)
else:
return queryset

View File

@ -7,7 +7,7 @@ Created on: 02.12.20
"""
from dal import autocomplete
from django import forms
from django.contrib.auth.models import User
from user.models import User
from django.db import transaction
from django.urls import reverse, reverse_lazy
from django.utils.translation import gettext_lazy as _

View File

@ -6,7 +6,7 @@ Created on: 27.09.21
"""
from dal import autocomplete
from django.contrib.auth.models import User
from user.models import User
from django.db import transaction
from django import forms
from django.urls import reverse

View File

@ -8,7 +8,7 @@ Created on: 15.11.21
import shutil
from django.contrib import messages
from django.contrib.auth.models import User
from user.models import User
from django.db import models, transaction
from django.db.models import QuerySet
from django.http import HttpRequest

View File

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

View File

@ -6,7 +6,7 @@ Created on: 07.12.20
"""
from dal_select2.views import Select2QuerySetView, Select2GroupQuerySetView
from django.contrib.auth.models import User
from user.models import User
from django.db.models import Q
from codelist.models import KonovaCode

View File

@ -8,7 +8,7 @@ Created on: 16.11.20
from django.http import HttpRequest
from konova.sub_settings.context_settings import BASE_TITLE, HELP_LINK, BASE_FRONTEND_TITLE
from konova.sub_settings.django_settings import LANGUAGE_CODE
from konova.sub_settings.django_settings import EMAIL_REPLY_TO
class BaseContext:
@ -24,7 +24,8 @@ class BaseContext:
"language": request.LANGUAGE_CODE,
"user": request.user,
"current_role": None,
"help_link": HELP_LINK
"help_link": HELP_LINK,
"CONTACT_MAIL": EMAIL_REPLY_TO,
}
# Add additional context, derived from given parameters

View File

@ -0,0 +1,7 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 12.01.22
"""

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

@ -0,0 +1,406 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 12.01.22
"""
import django_filters
from django import forms
from django.db.models import QuerySet, Q
from django.utils.translation import gettext_lazy as _
from dal_select2.widgets import ModelSelect2
from codelist.models import KonovaCode
from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID, CODELIST_REGISTRATION_OFFICE_ID
from intervention.inputs import DummyFilterInput
from konova.models import Parcel, District
class KeywordTableFilterMixin(django_filters.FilterSet):
q = django_filters.Filter(
method='filter_by_keyword',
# Since we use a custom search bar in the template, we need to 'render' this filter
# as 'anonymous' HiddenInput (no id, no name). This way our custom search bar's id and name won't be
# overwritten with these id and name (which would be equal)
# This way we can use the simple filter method mapping for a parameter without using a predefined widget!
widget=DummyFilterInput(),
)
class Meta:
abstract = True
def filter_by_keyword(self, queryset, name, value) -> QuerySet:
""" Filters queryset depending on value of search bar input
Args:
queryset ():
name ():
value ():
Returns:
"""
value = value.strip()
# build filter expression
q = Q(title__icontains=value) | Q(identifier__icontains=value)
return queryset.filter(q)
class FileNumberTableFilterMixin(django_filters.FilterSet):
rf = django_filters.CharFilter(
method="filter_file_number",
label=_(""),
label_suffix=_(""),
widget=forms.TextInput(
attrs={
"placeholder": _("File number"),
"title": _("Search for file number"),
"class": "form-control",
}
),
)
def filter_file_number(self, queryset, name, value) -> QuerySet:
queryset = queryset.filter(
Q(responsible__registration_file_number__icontains=value) |
Q(responsible__conservation_file_number__icontains=value)
)
return queryset
class GeoReferencedTableFilterMixin(django_filters.FilterSet):
""" A mixin for AbstractTableFilter
Specialized on filtering GeoReferenced model types
"""
# Parcel gmrkng
di = django_filters.CharFilter(
method="filter_district",
label=_(""),
label_suffix=_(""),
widget=forms.TextInput(
attrs={
"placeholder": _("District"),
"title": _("Search for district"),
"class": "form-control",
}
),
)
# Parcel gmrkng
pg = django_filters.CharFilter(
method="filter_gmrkng",
label=_(""),
label_suffix=_(""),
widget=forms.TextInput(
attrs={
"placeholder": _("Parcel gmrkng"),
"title": _("Search for parcel gmrkng"),
"class": "form-control",
}
),
)
# Parcel
p = django_filters.CharFilter(
method="filter_parcel",
label=_(""),
label_suffix=_(""),
widget=forms.TextInput(
attrs={
"placeholder": _("Parcel"),
"title": _("Search for parcel"),
"class": "form-control",
}
),
)
# Parcel counter
pc = django_filters.CharFilter(
method="filter_parcel_counter",
label=_(""),
label_suffix=_(""),
widget=forms.TextInput(
attrs={
"placeholder": _("Parcel counter"),
"title": _("Search for parcel counter"),
"class": "form-control",
}
),
)
# Parcel counter
pn = django_filters.CharFilter(
method="filter_parcel_number",
label=_(""),
label_suffix=_(""),
widget=forms.TextInput(
attrs={
"placeholder": _("Parcel number"),
"title": _("Search for parcel number"),
"class": "form-control",
}
),
)
class Meta:
abstract = True
def _filter_parcel_reference(self, queryset, name, value, filter_value) -> QuerySet:
""" Filters the parcel entries by a given filter_value.
filter_value may already include further filter annotations like 'xy__icontains'
Args:
queryset ():
name ():
value ():
filter_value ():
Returns:
"""
_filter = {
filter_value: value
}
matching_parcels = Parcel.objects.filter(
**_filter
)
related_geoms = matching_parcels.values(
"geometries"
).distinct()
queryset = queryset.filter(
geometry__id__in=related_geoms
)
return queryset
def filter_district(self, queryset, name, value) -> QuerySet:
""" Filters queryset depending on value for 'Gemarkung'
Args:
queryset ():
name ():
value ():
Returns:
"""
matching_districts = District.objects.filter(
krs=value
)
matching_parcels = Parcel.objects.filter(
district__in=matching_districts
)
related_geoms = matching_parcels.values(
"geometries"
).distinct()
queryset = queryset.filter(
geometry__id__in=related_geoms
)
return queryset
def filter_gmrkng(self, queryset, name, value) -> QuerySet:
""" Filters queryset depending on value for 'Gemarkung'
Args:
queryset ():
name ():
value ():
Returns:
"""
queryset = self._filter_parcel_reference(queryset, name, value, "gmrkng__istartswith")
return queryset
def filter_parcel(self, queryset, name, value) -> QuerySet:
""" Filters queryset depending on value for 'Parcel'
Args:
queryset ():
name ():
value ():
Returns:
"""
value = value.replace("-", "")
queryset = self._filter_parcel_reference(queryset, name, value, "flr")
return queryset
def filter_parcel_counter(self, queryset, name, value) -> QuerySet:
""" Filters queryset depending on value for 'Parcel'
Args:
queryset ():
name ():
value ():
Returns:
"""
value = value.replace("-", "")
queryset = self._filter_parcel_reference(queryset, name, value, "flrstck_zhlr")
return queryset
def filter_parcel_number(self, queryset, name, value) -> QuerySet:
""" Filters queryset depending on value for 'Parcel'
Args:
queryset ():
name ():
value ():
Returns:
"""
value = value.replace("-", "")
queryset = self._filter_parcel_reference(queryset, name, value, "flrstck_nnr")
return queryset
class ShareableTableFilterMixin(django_filters.FilterSet):
""" A mixin for AbstractTableFilter
Specialized on filtering shareable model types
"""
sa = django_filters.BooleanFilter(
method='filter_show_all',
label=_("Show unshared"),
label_suffix=_(""),
widget=forms.CheckboxInput(
attrs={
"class": "form-check-input",
}
)
)
class Meta:
abstract = True
def __init__(self, *args, **kwargs):
self.user = kwargs.pop("user", None)
if self.user is None:
raise AttributeError("User must be set for further filtering!")
super().__init__(*args, **kwargs)
def filter_show_all(self, queryset, name, value) -> QuerySet:
""" Filters queryset depending on value of 'show_all' setting
Args:
queryset ():
name ():
value ():
Returns:
"""
if not value:
return queryset.filter(
users__in=[self.user], # requesting user has access
)
else:
return queryset
class RecordableTableFilterMixin(django_filters.FilterSet):
""" A mixin for AbstractTableFilter
Specialized on filtering recordable model types
"""
sr = django_filters.BooleanFilter(
method='filter_show_recorded',
label=_("Show recorded"),
label_suffix=_(""),
widget=forms.CheckboxInput(
attrs={
"class": "form-check-input",
}
)
)
class Meta:
abstract = True
def filter_show_recorded(self, queryset, name, value) -> QuerySet:
""" Filters queryset depending on value of 'show_recorded' setting
Args:
queryset ():
name ():
value ():
Returns:
"""
if not value:
return queryset.filter(
recorded=None,
)
else:
return queryset
class RegistrationOfficeTableFilterMixin(django_filters.FilterSet):
""" A mixin for AbstractTableFilter
Specialized on filtering for related registration offices
"""
ro = django_filters.ModelChoiceFilter(
method="filter_reg_office",
label=_(""),
label_suffix=_(""),
queryset=KonovaCode.objects.filter(
is_archived=False,
is_leaf=True,
code_lists__in=[CODELIST_REGISTRATION_OFFICE_ID],
),
widget=ModelSelect2(
url="codes-registration-office-autocomplete",
attrs={
"data-placeholder": _("Registration office"),
"title": _("Search for registration office"),
"class": "",
}
),
)
def filter_reg_office(self, queryset, name, value):
qs = queryset.filter(
responsible__registration_office=value
)
return qs
class ConservationOfficeTableFilterMixin(django_filters.FilterSet):
""" A mixin for AbstractTableFilter
Specialized on filtering for related conservation offices
"""
co = django_filters.ModelChoiceFilter(
method="filter_cons_office",
label=_(""),
label_suffix=_(""),
queryset=KonovaCode.objects.filter(
is_archived=False,
is_leaf=True,
code_lists__in=[CODELIST_CONSERVATION_OFFICE_ID],
),
widget=ModelSelect2(
url="codes-conservation-office-autocomplete",
attrs={
"data-placeholder": _("Conservation office"),
"title": _("Search for conservation office"),
"class": "",
}
),
)
def filter_cons_office(self, queryset, name, value):
qs = queryset.filter(
responsible__conservation_office=value
)
return qs

View File

@ -0,0 +1,50 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 12.01.22
"""
import django_filters
from konova.filters.mixins import RegistrationOfficeTableFilterMixin, ConservationOfficeTableFilterMixin, \
KeywordTableFilterMixin, FileNumberTableFilterMixin, GeoReferencedTableFilterMixin, ShareableTableFilterMixin, \
RecordableTableFilterMixin
class AbstractTableFilter(django_filters.FilterSet):
""" Base TableFilter for all models
"""
selection_filter = None
query_filter = None
checkbox_filter = None
qs = None
user = None
class Meta:
abstract = True
class SelectionTableFilter(RegistrationOfficeTableFilterMixin,
ConservationOfficeTableFilterMixin):
""" TableFilter holding different filter options for selection related filtering
"""
pass
class QueryTableFilter(KeywordTableFilterMixin,
FileNumberTableFilterMixin,
GeoReferencedTableFilterMixin):
""" TableFilter holding different filter options for query related filtering
"""
pass
class CheckboxTableFilter(ShareableTableFilterMixin, RecordableTableFilterMixin):
""" TableFilter holding different filter options for checkbox related filtering
"""
pass

View File

@ -12,7 +12,7 @@ from bootstrap_modal_forms.forms import BSModalForm
from bootstrap_modal_forms.utils import is_ajax
from django import forms
from django.contrib import messages
from django.contrib.auth.models import User
from user.models import User
from django.contrib.gis.forms import OSMWidget, MultiPolygonField
from django.contrib.gis.geos import MultiPolygon
from django.db import transaction

View File

@ -7,7 +7,8 @@ Created on: 15.12.20
"""
from getpass import getpass
from django.contrib.auth.models import User, Group
from user.models import User
from django.contrib.auth.models import Group
from django.core.management import BaseCommand, call_command
from django.db import transaction

View File

@ -23,10 +23,9 @@ GROUPS_DATA = [
# Must match with UserNotificationEnum
USER_NOTIFICATIONS_NAMES = {
"NOTIFY_ON_NEW_RELATED_DATA": _("On new related data"),
"NOTIFY_ON_SHARE_LINK_DISABLED": _("On disabled share link"),
"NOTIFY_ON_SHARED_ACCESS_GAINED": _("On shared access gained"),
"NOTIFY_ON_SHARED_ACCESS_REMOVED": _("On shared access removed"),
"NOTIFY_ON_SHARED_DATA_RECORDED": _("On shared data recorded"),
"NOTIFY_ON_SHARED_DATA_DELETED": _("On shared data deleted"),
"NOTIFY_ON_REGISTERED_DATA_EDITED": _("On registered data edited"),
"NOTIFY_ON_SHARED_DATA_CHECKED": _("On shared data checked"),
}

View File

@ -110,9 +110,13 @@ class Geometry(BaseResource):
underlying_parcels = []
for result in fetched_parcels:
fetched_parcel = result[typename]
# There could be parcels which include the word 'Flur',
# which needs to be deleted and just keep the numerical values
## THIS CAN BE REMOVED IN THE FUTURE, WHEN 'Flur' WON'T OCCUR ANYMORE!
flr_val = fetched_parcel["ave:flur"].replace("Flur ", "")
parcel_obj = Parcel.objects.get_or_create(
gmrkng=fetched_parcel["ave:gemarkung"],
flr=fetched_parcel["ave:flur"],
flr=flr_val,
flrstck_nnr=fetched_parcel['ave:flstnrnen'],
flrstck_zhlr=fetched_parcel['ave:flstnrzae'],
)[0]

View File

@ -10,7 +10,11 @@ import uuid
from abc import abstractmethod
from django.contrib import messages
from django.contrib.auth.models import User
from konova.tasks import celery_send_mail_shared_access_removed, celery_send_mail_shared_access_given, \
celery_send_mail_shared_data_recorded, celery_send_mail_shared_data_unrecorded, \
celery_send_mail_shared_data_deleted, celery_send_mail_shared_data_checked
from user.models import User
from django.core.exceptions import ObjectDoesNotExist
from django.http import HttpRequest
from django.utils.timezone import now
@ -118,6 +122,12 @@ class BaseObject(BaseResource):
action = UserActionLogEntry.get_deleted_action(user)
self.deleted = action
self.log.add(action)
# Send mail
shared_users = self.users.all().values_list("id", flat=True)
for user_id in shared_users:
celery_send_mail_shared_data_deleted.delay(self.identifier, user_id)
self.save()
def add_log_entry(self, action: UserAction, user: User, comment: str):
@ -216,6 +226,11 @@ class RecordableObjectMixin(models.Model):
self.recorded = None
self.save()
self.log.add(action)
shared_users = self.users.all().values_list("id", flat=True)
for user_id in shared_users:
celery_send_mail_shared_data_unrecorded(self.identifier, user_id)
return action
def set_recorded(self, user: User):
@ -233,6 +248,11 @@ class RecordableObjectMixin(models.Model):
self.recorded = action
self.save()
self.log.add(action)
shared_users = self.users.all().values_list("id", flat=True)
for user_id in shared_users:
celery_send_mail_shared_data_recorded(self.identifier, user_id)
return action
def mark_as_edited(self, performing_user: User, request: HttpRequest = None, edit_comment: str = None):
@ -305,6 +325,12 @@ class CheckableObjectMixin(models.Model):
action = UserActionLogEntry.get_checked_action(user)
self.checked = action
self.save()
# Send mail
shared_users = self.users.all().values_list("id", flat=True)
for user_id in shared_users:
celery_send_mail_shared_data_checked.delay(self.identifier, user_id)
self.log.add(action)
return action
@ -411,6 +437,17 @@ class ShareableObjectMixin(models.Model):
users = User.objects.filter(
id__in=accessing_users
)
removed_users = self.users.all().exclude(
id__in=accessing_users
).values("id")
# Send mails
for user in removed_users:
celery_send_mail_shared_access_removed.delay(self.identifier, user["id"])
for user in new_accessing_users:
celery_send_mail_shared_access_given.delay(self.identifier, user)
# Set new shared users
self.share_with_list(users)

View File

@ -5,9 +5,10 @@ Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 17.08.21
"""
from django.contrib.auth.models import User
from simple_sso.sso_client.client import Client
from user.models import User
class KonovaSSOClient(Client):
""" Konova specialized derivate of general sso.Client.

View File

@ -130,7 +130,7 @@ DATABASES = {
# Password validation
# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators
AUTH_USER_MODEL = "user.User"
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
@ -205,9 +205,15 @@ DEBUG_TOOLBAR_CONFIG = {
}
# EMAIL (see https://docs.djangoproject.com/en/dev/topics/email/)
DEFAULT_FROM_EMAIL = "bot@arneo.de" # The default email address for the 'from' element
# CHANGE_ME !!! ONLY FOR DEVELOPMENT !!!
EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend'
EMAIL_FILE_PATH = '/tmp/app-messages' # change this to a proper location
DEFAULT_FROM_EMAIL = "service@ksp.de" # The default email address for the 'from' element
EMAIL_HOST = "localhost"
EMAIL_PORT = "1025"
EMAIL_REPLY_TO = "ksp-servicestelle@sgdnord.rlp.de"
EMAIL_PORT = "25"
#EMAIL_HOST_USER = ""
#EMAIL_HOST_PASSWORD = ""
EMAIL_USE_TLS = False

View File

@ -1,14 +1,60 @@
from time import sleep
from celery import shared_task
from django.core.exceptions import ObjectDoesNotExist
from konova.models import Geometry
@shared_task
def celery_update_parcels(geometry_id: str):
def celery_update_parcels(geometry_id: str, recheck: bool = True):
from konova.models import Geometry
try:
geom = Geometry.objects.get(id=geometry_id)
geom.parcels.clear()
geom.update_parcels()
except ObjectDoesNotExist:
return
if recheck:
sleep(5)
celery_update_parcels(geometry_id, False)
@shared_task
def celery_send_mail_shared_access_removed(obj_identifier, user_id):
from user.models import User
user = User.objects.get(id=user_id)
user.send_mail_shared_access_removed(obj_identifier)
@shared_task
def celery_send_mail_shared_access_given(obj_identifier, user_id):
from user.models import User
user = User.objects.get(id=user_id)
user.send_mail_shared_access_given(obj_identifier)
@shared_task
def celery_send_mail_shared_data_recorded(obj_identifier, user_id):
from user.models import User
user = User.objects.get(id=user_id)
user.send_mail_shared_data_recorded(obj_identifier)
@shared_task
def celery_send_mail_shared_data_unrecorded(obj_identifier, user_id):
from user.models import User
user = User.objects.get(id=user_id)
user.send_mail_shared_data_unrecorded(obj_identifier)
@shared_task
def celery_send_mail_shared_data_deleted(obj_identifier, user_id):
from user.models import User
user = User.objects.get(id=user_id)
user.send_mail_shared_data_deleted(obj_identifier)
@shared_task
def celery_send_mail_shared_data_checked(obj_identifier, user_id):
from user.models import User
user = User.objects.get(id=user_id)
user.send_mail_shared_data_checked(obj_identifier)

View File

@ -7,7 +7,8 @@ Created on: 26.10.21
"""
import datetime
from django.contrib.auth.models import User, Group
from user.models import User
from django.contrib.auth.models import Group
from django.contrib.gis.geos import MultiPolygon, Polygon
from django.core.exceptions import ObjectDoesNotExist
from django.test import TestCase, Client
@ -404,6 +405,7 @@ class AutocompleteTestCase(BaseViewTestCase):
cls.atcmplt_code_comp_process = reverse("codes-process-type-autocomplete")
cls.atcmplt_code_comp_reg_off = reverse("codes-registration-office-autocomplete")
cls.atcmplt_code_comp_cons_off = reverse("codes-conservation-office-autocomplete")
cls.atcmplt_code_share_user = reverse("share-user-autocomplete")
def _test_views_anonymous_user(self):
# ATTENTION: As of the current state of django-autocomplete-light, there is no way to check on authenticated
@ -421,6 +423,7 @@ class AutocompleteTestCase(BaseViewTestCase):
self.atcmplt_code_comp_process,
self.atcmplt_code_comp_reg_off,
self.atcmplt_code_comp_cons_off,
self.atcmplt_code_share_user,
]
self.assert_url_fail(client, urls)

View File

@ -57,4 +57,7 @@ urlpatterns = [
if DEBUG:
urlpatterns += [
path('__debug__/', include(debug_toolbar.urls)),
]
]
handler404 = "konova.views.get_404_view"
handler500 = "konova.views.get_500_view"

View File

@ -8,8 +8,10 @@ Created on: 09.11.20
import logging
from django.core.mail import send_mail
from django.template.loader import render_to_string
from django.utils.translation import gettext_lazy as _
from konova.sub_settings.django_settings import DEFAULT_FROM_EMAIL
from konova.sub_settings.django_settings import DEFAULT_FROM_EMAIL, EMAIL_REPLY_TO
logger = logging.getLogger(__name__)
@ -26,27 +28,156 @@ class Mailer:
auth_user = None
auth_password = None
def __init__(self, to_mail: list, from_mail: str = DEFAULT_FROM_EMAIL, auth_user: str = None, auth_password: str = None, fail_silently: bool = False):
# Make sure given to_mail parameter is a list
if isinstance(to_mail, str):
to_mail = [to_mail]
def __init__(self, from_mail: str = DEFAULT_FROM_EMAIL, auth_user: str = None, auth_password: str = None, fail_silently: bool = False):
self.from_mail = from_mail
self.to_mail = to_mail
self.fail_silently = fail_silently
self.auth_user = auth_user
self.auth_password = auth_password
def send(self, subject: str, msg: str):
def send(self, recipient_list: list, subject: str, msg: str):
"""
Sends a mail with subject and message
"""
return send_mail(
subject=subject,
message=msg,
message=None,
html_message=msg,
from_email=self.from_mail,
recipient_list=self.to_mail,
recipient_list=recipient_list,
fail_silently=self.fail_silently,
auth_user=self.auth_user,
auth_password=self.auth_password
)
)
def send_mail_shared_access_removed(self, obj_identifier, user):
""" Send a mail if user has no access to the object anymore
Args:
obj_identifier (str): The object identifier
Returns:
"""
context = {
"user": user,
"obj_identifier": obj_identifier,
"EMAIL_REPLY_TO": EMAIL_REPLY_TO,
}
msg = render_to_string("email/sharing/shared_access_removed.html", context)
user_mail_address = [user.email]
self.send(
user_mail_address,
_("{} - Shared access removed").format(obj_identifier),
msg
)
def send_mail_shared_access_given(self, obj_identifier, user):
""" Send a mail if user just got access to the object
Args:
obj_identifier (str): The object identifier
Returns:
"""
context = {
"user": user,
"obj_identifier": obj_identifier,
"EMAIL_REPLY_TO": EMAIL_REPLY_TO,
}
msg = render_to_string("email/sharing/shared_access_given.html", context)
user_mail_address = [user.email]
self.send(
user_mail_address,
_("{} - Shared access given").format(obj_identifier),
msg
)
def send_mail_shared_data_recorded(self, obj_identifier, user):
""" Send a mail if the user's shared data has just been unrecorded
Args:
obj_identifier (str): The object identifier
Returns:
"""
context = {
"user": user,
"obj_identifier": obj_identifier,
"EMAIL_REPLY_TO": EMAIL_REPLY_TO,
}
msg = render_to_string("email/recording/shared_data_recorded.html", context)
user_mail_address = [user.email]
self.send(
user_mail_address,
_("{} - Shared data recorded").format(obj_identifier),
msg
)
def send_mail_shared_data_unrecorded(self, obj_identifier, user):
""" Send a mail if the user's shared data has just been unrecorded
Args:
obj_identifier (str): The object identifier
Returns:
"""
context = {
"user": user,
"obj_identifier": obj_identifier,
"EMAIL_REPLY_TO": EMAIL_REPLY_TO,
}
msg = render_to_string("email/recording/shared_data_unrecorded.html", context)
user_mail_address = [user.email]
self.send(
user_mail_address,
_("{} - Shared data unrecorded").format(obj_identifier),
msg
)
def send_mail_shared_data_deleted(self, obj_identifier, user):
""" Send a mail if shared data has just been deleted
Args:
obj_identifier (str): The object identifier
Returns:
"""
context = {
"user": user,
"obj_identifier": obj_identifier,
"EMAIL_REPLY_TO": EMAIL_REPLY_TO,
}
msg = render_to_string("email/deleting/shared_data_deleted.html", context)
user_mail_address = [user.email]
self.send(
user_mail_address,
_("{} - Shared data deleted").format(obj_identifier),
msg
)
def send_mail_shared_data_checked(self, obj_identifier, user):
""" Send a mail if shared data just has been checked
Args:
obj_identifier (str): The object identifier
Returns:
"""
context = {
"user": user,
"obj_identifier": obj_identifier,
"EMAIL_REPLY_TO": EMAIL_REPLY_TO,
}
msg = render_to_string("email/checking/shared_data_checked.html", context)
user_mail_address = [user.email]
self.send(
user_mail_address,
_("{} - Shared data checked").format(obj_identifier),
msg
)

View File

@ -8,7 +8,7 @@ Created on: 17.08.21
from collections import Iterable
import requests
from django.contrib.auth.models import User
from user.models import User
from django.utils.translation import gettext_lazy as _
from konova.settings import SSO_SERVER_BASE, SSO_PUBLIC_KEY, PROXIES

View File

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

View File

@ -5,7 +5,7 @@ Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 02.07.21
"""
from django.contrib.auth.models import User
from user.models import User
from konova.settings import ETS_GROUP, ZB_GROUP

View File

@ -6,6 +6,7 @@ Created on: 17.12.21
"""
from abc import abstractmethod
from time import sleep
import requests
import xmltodict
@ -122,6 +123,7 @@ class ParcelWFSFetcher(AbstractWFSFetcher):
spatial_operator: str = "Intersects",
filter_srid: str = None,
start_index: int = 0,
rerun_on_exception: bool = True
):
""" Fetches features from the WFS using POST
@ -159,6 +161,23 @@ class ParcelWFSFetcher(AbstractWFSFetcher):
{},
)
# Check if collection is an exception and does not contain the requested data
if len(collection) == 0:
exception = content.get(
"ows:ExceptionReport",
{}
)
if len(exception) > 0 and rerun_on_exception:
# Wait a second before another try
sleep(1)
self.get_features(
typenames,
spatial_operator,
filter_srid,
start_index,
rerun_on_exception=False
)
members = collection.get(
"wfs:member",
[],

View File

@ -113,4 +113,31 @@ def remove_deadline_view(request: HttpRequest, id:str):
return form.process_request(
request,
msg_success=_("Deadline removed")
)
)
def get_404_view(request: HttpRequest, exception=None):
""" Returns a 404 handling view
Args:
request ():
exception ():
Returns:
"""
context = BaseContext.context
return render(request, "404.html", context, status=404)
def get_500_view(request: HttpRequest):
""" Returns a 404 handling view
Args:
request ():
Returns:
"""
context = BaseContext.context
return render(request, "500.html", context, status=500)

Binary file not shown.

View File

@ -3,14 +3,21 @@
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#: compensation/filters.py:71 compensation/forms/modalForms.py:34
#: compensation/filters.py:122 compensation/forms/modalForms.py:34
#: compensation/forms/modalForms.py:45 compensation/forms/modalForms.py:61
#: compensation/forms/modalForms.py:238 compensation/forms/modalForms.py:316
#: intervention/filters.py:26 intervention/filters.py:40
#: intervention/filters.py:47 intervention/filters.py:48
#: intervention/forms/forms.py:52 intervention/forms/forms.py:154
#: intervention/forms/forms.py:166 intervention/forms/modalForms.py:125
#: intervention/forms/modalForms.py:138 intervention/forms/modalForms.py:151
#: konova/filters/mixins.py:53 konova/filters/mixins.py:54
#: konova/filters/mixins.py:81 konova/filters/mixins.py:82
#: konova/filters/mixins.py:94 konova/filters/mixins.py:95
#: konova/filters/mixins.py:107 konova/filters/mixins.py:108
#: konova/filters/mixins.py:120 konova/filters/mixins.py:121
#: konova/filters/mixins.py:134 konova/filters/mixins.py:135
#: konova/filters/mixins.py:270 konova/filters/mixins.py:315
#: konova/filters/mixins.py:353 konova/filters/mixins.py:354
#: konova/filters/mixins.py:385 konova/filters/mixins.py:386
#: konova/forms.py:140 konova/forms.py:241 konova/forms.py:312
#: konova/forms.py:339 konova/forms.py:349 konova/forms.py:362
#: konova/forms.py:374 konova/forms.py:392 user/forms.py:38
@ -19,7 +26,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-01-07 15:32+0100\n"
"POT-Creation-Date: 2022-01-12 16:43+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -40,12 +47,12 @@ msgstr "Bis"
#: analysis/forms.py:47 compensation/forms/forms.py:77
#: compensation/templates/compensation/detail/eco_account/view.html:58
#: compensation/templates/compensation/report/eco_account/report.html:16
#: compensation/utils/quality.py:112 ema/templates/ema/detail/view.html:49
#: compensation/utils/quality.py:100 ema/templates/ema/detail/view.html:49
#: ema/templates/ema/report/report.html:16 ema/utils/quality.py:26
#: intervention/forms/forms.py:100
#: intervention/templates/intervention/detail/view.html:56
#: intervention/templates/intervention/report/report.html:37
#: intervention/utils/quality.py:49
#: intervention/utils/quality.py:49 konova/filters/mixins.py:395
msgid "Conservation office"
msgstr "Eintragungsstelle"
@ -133,7 +140,7 @@ msgstr "Zuständigkeitsbereich"
#: compensation/templates/compensation/detail/compensation/view.html:63
#: intervention/tables.py:33
#: intervention/templates/intervention/detail/view.html:68
#: user/models/user_action.py:19
#: user/models/user_action.py:18
msgid "Checked"
msgstr "Geprüft"
@ -145,14 +152,14 @@ msgstr "Geprüft"
#: analysis/templates/analysis/reports/includes/intervention/compensated_by.html:9
#: analysis/templates/analysis/reports/includes/intervention/laws.html:20
#: analysis/templates/analysis/reports/includes/old_data/amount.html:18
#: compensation/tables.py:41 compensation/tables.py:181
#: compensation/tables.py:41 compensation/tables.py:182
#: compensation/templates/compensation/detail/compensation/view.html:77
#: compensation/templates/compensation/detail/eco_account/includes/deductions.html:31
#: compensation/templates/compensation/detail/eco_account/view.html:44
#: ema/tables.py:38 ema/templates/ema/detail/view.html:35
#: intervention/tables.py:39
#: intervention/templates/intervention/detail/view.html:82
#: user/models/user_action.py:20
#: user/models/user_action.py:19
msgid "Recorded"
msgstr "Verzeichnet"
@ -231,7 +238,7 @@ msgstr "Kompensationsart"
#: analysis/templates/analysis/reports/includes/intervention/compensated_by.html:15
#: analysis/templates/analysis/reports/includes/old_data/amount.html:29
#: compensation/tables.py:84
#: compensation/tables.py:85
#: compensation/templates/compensation/detail/compensation/view.html:19
#: konova/templates/konova/home.html:49 templates/navbars/navbar.html:28
msgid "Compensation"
@ -276,14 +283,14 @@ msgstr "Typ"
#: analysis/templates/analysis/reports/includes/old_data/amount.html:24
#: intervention/forms/modalForms.py:306 intervention/forms/modalForms.py:313
#: intervention/tables.py:88
#: intervention/tables.py:89
#: intervention/templates/intervention/detail/view.html:19
#: konova/templates/konova/home.html:11 templates/navbars/navbar.html:22
msgid "Intervention"
msgstr "Eingriff"
#: analysis/templates/analysis/reports/includes/old_data/amount.html:34
#: compensation/tables.py:224
#: compensation/tables.py:226
#: compensation/templates/compensation/detail/eco_account/view.html:19
#: intervention/forms/modalForms.py:279 intervention/forms/modalForms.py:286
#: konova/templates/konova/home.html:88 templates/navbars/navbar.html:34
@ -298,12 +305,12 @@ msgstr "Altfälle"
msgid "Before"
msgstr "Vor"
#: compensation/filters.py:70
#: compensation/filters.py:121
msgid "Show only unrecorded"
msgstr "Nur unverzeichnete anzeigen"
#: compensation/forms/forms.py:32 compensation/tables.py:25
#: compensation/tables.py:166 ema/tables.py:28 intervention/forms/forms.py:26
#: compensation/tables.py:167 ema/tables.py:28 intervention/forms/forms.py:26
#: intervention/tables.py:23
#: intervention/templates/intervention/detail/includes/compensations.html:30
msgid "Identifier"
@ -314,7 +321,7 @@ msgid "Generated automatically"
msgstr "Automatisch generiert"
#: compensation/forms/forms.py:44 compensation/tables.py:30
#: compensation/tables.py:171
#: compensation/tables.py:172
#: compensation/templates/compensation/detail/compensation/includes/documents.html:28
#: compensation/templates/compensation/detail/compensation/view.html:31
#: compensation/templates/compensation/detail/eco_account/includes/documents.html:28
@ -367,7 +374,7 @@ msgstr "Zusätzlicher Kommentar"
#: compensation/forms/forms.py:93
#: compensation/templates/compensation/detail/eco_account/view.html:62
#: compensation/templates/compensation/report/eco_account/report.html:20
#: compensation/utils/quality.py:114 ema/templates/ema/detail/view.html:53
#: compensation/utils/quality.py:102 ema/templates/ema/detail/view.html:53
#: ema/templates/ema/report/report.html:20 ema/utils/quality.py:28
#: intervention/forms/forms.py:128
#: intervention/templates/intervention/detail/view.html:60
@ -427,7 +434,7 @@ msgstr "Neue Kompensation"
msgid "Edit compensation"
msgstr "Bearbeite Kompensation"
#: compensation/forms/forms.py:302 compensation/utils/quality.py:96
#: compensation/forms/forms.py:302 compensation/utils/quality.py:84
msgid "Available Surface"
msgstr "Verfügbare Fläche"
@ -437,7 +444,7 @@ msgstr "Die für Abbuchungen zur Verfügung stehende Menge"
#: compensation/forms/forms.py:314
#: compensation/templates/compensation/detail/eco_account/view.html:66
#: compensation/utils/quality.py:84
#: compensation/utils/quality.py:72
msgid "Agreement date"
msgstr "Vereinbarungsdatum"
@ -633,65 +640,65 @@ msgstr ""
"Es wurde bereits mehr Fläche abgebucht, als Sie nun als abbuchbar einstellen "
"wollen. Kontaktieren Sie die für die Abbuchungen verantwortlichen Nutzer!"
#: compensation/tables.py:47 compensation/tables.py:187 ema/tables.py:44
#: compensation/tables.py:47 compensation/tables.py:188 ema/tables.py:44
#: intervention/tables.py:51
msgid "Editable"
msgstr "Freigegeben"
#: compensation/tables.py:53 compensation/tables.py:193 ema/tables.py:50
#: compensation/tables.py:53 compensation/tables.py:194 ema/tables.py:50
#: intervention/tables.py:57
msgid "Last edit"
msgstr "Zuletzt bearbeitet"
#: compensation/tables.py:84 compensation/tables.py:224 ema/tables.py:82
#: intervention/tables.py:88
#: compensation/tables.py:85 compensation/tables.py:226 ema/tables.py:83
#: intervention/tables.py:89
msgid "Open {}"
msgstr "Öffne {}"
#: compensation/tables.py:105 intervention/tables.py:107
#: compensation/tables.py:106 intervention/tables.py:108
msgid "Not checked yet"
msgstr "Noch nicht geprüft"
#: compensation/tables.py:110 intervention/tables.py:112
#: compensation/tables.py:111 intervention/tables.py:113
msgid "Checked on {} by {}"
msgstr "Am {} von {} geprüft worden"
#: compensation/tables.py:129
#: compensation/tables.py:130
#: compensation/templates/compensation/detail/compensation/view.html:80
#: compensation/templates/compensation/detail/eco_account/includes/deductions.html:56
#: compensation/templates/compensation/detail/eco_account/view.html:47
#: ema/tables.py:101 ema/templates/ema/detail/view.html:38
#: intervention/tables.py:131
#: ema/tables.py:102 ema/templates/ema/detail/view.html:38
#: intervention/tables.py:132
#: intervention/templates/intervention/detail/view.html:85
msgid "Not recorded yet"
msgstr "Noch nicht verzeichnet"
#: compensation/tables.py:134 compensation/tables.py:262 ema/tables.py:106
#: intervention/tables.py:136
#: compensation/tables.py:135 compensation/tables.py:264 ema/tables.py:107
#: intervention/tables.py:137
msgid "Recorded on {} by {}"
msgstr "Am {} von {} verzeichnet worden"
#: compensation/tables.py:158 compensation/tables.py:284 ema/tables.py:129
#: intervention/tables.py:159
#: compensation/tables.py:159 compensation/tables.py:286 ema/tables.py:130
#: intervention/tables.py:160
msgid "Full access granted"
msgstr "Für Sie freigegeben - Datensatz kann bearbeitet werden"
#: compensation/tables.py:158 compensation/tables.py:284 ema/tables.py:129
#: intervention/tables.py:159
#: compensation/tables.py:159 compensation/tables.py:286 ema/tables.py:130
#: intervention/tables.py:160
msgid "Access not granted"
msgstr "Nicht freigegeben - Datensatz nur lesbar"
#: compensation/tables.py:176
#: compensation/tables.py:177
#: compensation/templates/compensation/detail/eco_account/view.html:35
#: konova/templates/konova/widgets/progressbar.html:3
msgid "Available"
msgstr "Verfügbar"
#: compensation/tables.py:202
#: compensation/tables.py:203
msgid "Eco Accounts"
msgstr "Ökokonten"
#: compensation/tables.py:257
#: compensation/tables.py:259
msgid "Not recorded yet. Can not be used for deductions, yet."
msgstr ""
"Noch nicht verzeichnet. Kann noch nicht für Abbuchungen genutzt werden."
@ -955,7 +962,7 @@ msgstr "Eingriffskennung"
#: compensation/templates/compensation/detail/eco_account/includes/deductions.html:37
#: intervention/templates/intervention/detail/includes/deductions.html:34
#: user/models/user_action.py:22
#: user/models/user_action.py:21
msgid "Created"
msgstr "Erstellt"
@ -1033,21 +1040,17 @@ msgstr "-"
msgid "States unequal"
msgstr "Ungleiche Zustandsflächenmengen"
#: compensation/utils/quality.py:72
msgid "Not recorded"
msgstr "Noch nicht verzeichnet"
#: compensation/utils/quality.py:86 intervention/utils/quality.py:84
#: compensation/utils/quality.py:74 intervention/utils/quality.py:84
msgid "Legal data"
msgstr "Rechtliche Daten"
#: compensation/utils/quality.py:100
#: compensation/utils/quality.py:88
msgid "Deductable surface can not be larger than state surface"
msgstr ""
"Die abbuchbare Fläche darf die Gesamtfläche der Zielzustände nicht "
"überschreiten"
#: compensation/utils/quality.py:116 ema/utils/quality.py:30
#: compensation/utils/quality.py:104 ema/utils/quality.py:30
#: intervention/utils/quality.py:55
msgid "Responsible data"
msgstr "Daten zu den verantwortlichen Stellen"
@ -1061,7 +1064,7 @@ msgid "Compensation {} edited"
msgstr "Kompensation {} bearbeitet"
#: compensation/views/compensation.py:230 compensation/views/eco_account.py:309
#: ema/views.py:183 intervention/views.py:477
#: ema/views.py:183 intervention/views.py:478
msgid "Log"
msgstr "Log"
@ -1116,36 +1119,36 @@ msgid "Deduction removed"
msgstr "Abbuchung entfernt"
#: compensation/views/eco_account.py:330 ema/views.py:263
#: intervention/views.py:519
#: intervention/views.py:520
msgid "{} unrecorded"
msgstr "{} entzeichnet"
#: compensation/views/eco_account.py:330 ema/views.py:263
#: intervention/views.py:519
#: intervention/views.py:520
msgid "{} recorded"
msgstr "{} verzeichnet"
#: compensation/views/eco_account.py:531 intervention/views.py:500
#: compensation/views/eco_account.py:531 intervention/views.py:501
msgid "Deduction added"
msgstr "Abbuchung hinzugefügt"
#: compensation/views/eco_account.py:616 ema/views.py:520
#: intervention/views.py:375
#: intervention/views.py:376
msgid "{} has already been shared with you"
msgstr "{} wurde bereits für Sie freigegeben"
#: compensation/views/eco_account.py:621 ema/views.py:525
#: intervention/views.py:380
#: intervention/views.py:381
msgid "{} has been shared with you"
msgstr "{} ist nun für Sie freigegeben"
#: compensation/views/eco_account.py:628 ema/views.py:532
#: intervention/views.py:387
#: intervention/views.py:388
msgid "Share link invalid"
msgstr "Freigabelink ungültig"
#: compensation/views/eco_account.py:651 ema/views.py:555
#: intervention/views.py:410
#: intervention/views.py:411
msgid "Share settings updated"
msgstr "Freigabe Einstellungen aktualisiert"
@ -1177,7 +1180,7 @@ msgstr ""
"Maßnahmen aus Ersatzzahlungen, die nach 2015 rechtskräftig wurden, werden "
"durch die Stiftung Natur und Umwelt verwaltet."
#: ema/tables.py:82 templates/navbars/navbar.html:43
#: ema/tables.py:83 templates/navbars/navbar.html:43
msgid "EMA"
msgstr ""
@ -1197,22 +1200,6 @@ msgstr "EMA {} bearbeitet"
msgid "EMA removed"
msgstr "EMA entfernt"
#: intervention/filters.py:25
msgid "Show unshared"
msgstr "Nicht freigegebene anzeigen"
#: intervention/filters.py:39
msgid "Show recorded"
msgstr "Verzeichnete anzeigen"
#: intervention/filters.py:51
msgid "District"
msgstr "Gemarkung"
#: intervention/filters.py:52
msgid "Search for district"
msgstr "Nach Gemarkung suchen"
#: intervention/forms/forms.py:44
msgid "Construction XY; Location ABC"
msgstr "Bauvorhaben XY; Flur ABC"
@ -1231,7 +1218,7 @@ msgstr "Mehrfachauswahl möglich"
#: intervention/forms/forms.py:84
#: intervention/templates/intervention/detail/view.html:48
#: intervention/templates/intervention/report/report.html:29
#: intervention/utils/quality.py:46
#: intervention/utils/quality.py:46 konova/filters/mixins.py:363
msgid "Registration office"
msgstr "Zulassungsbehörde"
@ -1382,11 +1369,11 @@ msgstr ""
msgid "Revocation"
msgstr "Widerspruch"
#: intervention/tables.py:176
#: intervention/tables.py:177
msgid "No revocation"
msgstr "Kein Widerspruch"
#: intervention/tables.py:182
#: intervention/tables.py:183
msgid "Revocation from {}, added on {} by {}"
msgstr "Widerspruch vom {}, am {} von {} hinzugefügt"
@ -1476,31 +1463,31 @@ msgstr ""
msgid "Intervention {} added"
msgstr "Eingriff {} hinzugefügt"
#: intervention/views.py:245
#: intervention/views.py:246
msgid "This intervention has {} revocations"
msgstr "Dem Eingriff liegen {} Widersprüche vor"
#: intervention/views.py:293
#: intervention/views.py:294
msgid "Intervention {} edited"
msgstr "Eingriff {} bearbeitet"
#: intervention/views.py:328
#: intervention/views.py:329
msgid "{} removed"
msgstr "{} entfernt"
#: intervention/views.py:349
#: intervention/views.py:350
msgid "Revocation removed"
msgstr "Widerspruch entfernt"
#: intervention/views.py:431
#: intervention/views.py:432
msgid "Check performed"
msgstr "Prüfung durchgeführt"
#: intervention/views.py:453
#: intervention/views.py:454
msgid "Revocation added"
msgstr "Widerspruch hinzugefügt"
#: intervention/views.py:524
#: intervention/views.py:525
msgid "There are errors on this intervention:"
msgstr "Es liegen Fehler in diesem Eingriff vor:"
@ -1521,6 +1508,73 @@ msgstr ""
"somit nichts eingeben, bearbeiten oder sonstige Aktionen ausführen. "
"Kontaktieren Sie bitte einen Administrator. +++"
#: konova/filters/mixins.py:57
msgid "File number"
msgstr "Aktenzeichen"
#: konova/filters/mixins.py:58
msgid "Search for file number"
msgstr "Nach Aktenzeichen suchen"
#: konova/filters/mixins.py:85
msgid "District"
msgstr "Kreis"
#: konova/filters/mixins.py:86
msgid "Search for district"
msgstr "Nach Kreis suchen"
#: konova/filters/mixins.py:98
msgid "Parcel gmrkng"
msgstr "Gemarkung"
#: konova/filters/mixins.py:99
msgid "Search for parcel gmrkng"
msgstr "Nach Gemarkung suchen"
#: konova/filters/mixins.py:111
#: konova/templates/konova/includes/parcels.html:18
msgid "Parcel"
msgstr "Flur"
#: konova/filters/mixins.py:112
msgid "Search for parcel"
msgstr "Nach Flur suchen"
#: konova/filters/mixins.py:124
#: konova/templates/konova/includes/parcels.html:19
msgid "Parcel counter"
msgstr "Flurstückzähler"
#: konova/filters/mixins.py:125
msgid "Search for parcel counter"
msgstr "Nach Flurstückzähler suchen"
#: konova/filters/mixins.py:138
#: konova/templates/konova/includes/parcels.html:20
msgid "Parcel number"
msgstr "Flurstücknenner"
#: konova/filters/mixins.py:139
msgid "Search for parcel number"
msgstr "Nach Flurstücknenner suchen"
#: konova/filters/mixins.py:269
msgid "Show unshared"
msgstr "Nicht freigegebene anzeigen"
#: konova/filters/mixins.py:314
msgid "Show recorded"
msgstr "Verzeichnete anzeigen"
#: konova/filters/mixins.py:364
msgid "Search for registration office"
msgstr "Nach Zulassungsbehörde suchen"
#: konova/filters/mixins.py:396
msgid "Search for conservation office"
msgstr "Nch Eintragungsstelle suchen"
#: konova/forms.py:37 templates/form/collapsable/form.html:62
msgid "Save"
msgstr "Speichern"
@ -1601,28 +1655,24 @@ msgstr ""
"Ich, {} {}, bestätige, dass diese Daten wieder entzeichnet werden müssen."
#: konova/management/commands/setup_data.py:26
msgid "On new related data"
msgstr "Wenn neue Daten für mich angelegt werden"
msgid "On shared access gained"
msgstr "Wenn mir eine Freigabe zu Daten erteilt wird"
#: konova/management/commands/setup_data.py:27
msgid "On disabled share link"
msgstr "Wenn ein Freigabelink deaktiviert wird"
#: konova/management/commands/setup_data.py:28
msgid "On shared access removed"
msgstr "Wenn mir eine Freigabe zu Daten entzogen wird"
#: konova/management/commands/setup_data.py:29
#: konova/management/commands/setup_data.py:28
msgid "On shared data recorded"
msgstr "Wenn meine freigegebenen Daten verzeichnet wurden"
#: konova/management/commands/setup_data.py:30
#: konova/management/commands/setup_data.py:29
msgid "On shared data deleted"
msgstr "Wenn meine freigegebenen Daten gelöscht wurden"
#: konova/management/commands/setup_data.py:31
msgid "On registered data edited"
msgstr "Wenn meine freigegebenen Daten bearbeitet wurden"
#: konova/management/commands/setup_data.py:30
msgid "On shared data checked"
msgstr "Wenn meine freigegebenen Daten geprüft wurden"
#: konova/models/deadline.py:18
msgid "Finished"
@ -1687,18 +1737,6 @@ msgstr "Kreis"
msgid "Gemarkung"
msgstr "Gemarkung"
#: konova/templates/konova/includes/parcels.html:18
msgid "Parcel"
msgstr "Flur"
#: konova/templates/konova/includes/parcels.html:19
msgid "Parcel counter"
msgstr "Flurstückzähler"
#: konova/templates/konova/includes/parcels.html:20
msgid "Parcel number"
msgstr "Flurstücknenner"
#: konova/templates/konova/widgets/generate-content-input.html:6
msgid "Generate new"
msgstr "Neu generieren"
@ -1715,6 +1753,30 @@ msgstr "In Zwischenablage kopiert"
msgid "Document '{}' deleted"
msgstr "Dokument '{}' gelöscht"
#: konova/utils/mailer.py:70
msgid "{} - Shared access removed"
msgstr "{} - Zugriff entzogen"
#: konova/utils/mailer.py:92
msgid "{} - Shared access given"
msgstr "{} - Zugriff freigegeben"
#: konova/utils/mailer.py:114
msgid "{} - Shared data recorded"
msgstr "{} - Freigegebene Daten verzeichnet"
#: konova/utils/mailer.py:136
msgid "{} - Shared data unrecorded"
msgstr "{} - Freigegebene Daten entzeichnet"
#: konova/utils/mailer.py:158
msgid "{} - Shared data deleted"
msgstr "{} - Freigegebene Daten gelöscht"
#: konova/utils/mailer.py:180
msgid "{} - Shared data checked"
msgstr "{} - Freigegebene Daten geprüft"
#: konova/utils/message_templates.py:11
msgid "There was an error on this form."
msgstr "Es gab einen Fehler im Formular."
@ -1829,6 +1891,173 @@ msgstr "Alle"
msgid "News"
msgstr "Neuigkeiten"
#: templates/404.html:7
msgid "Not found"
msgstr "Nicht gefunden"
#: templates/404.html:10
msgid "The requested data does not exist."
msgstr "Die angeforderten Daten existieren nicht."
#: templates/500.html:7
msgid "Server Error"
msgstr ""
#: templates/500.html:10
msgid "Something happened. We are working on it!"
msgstr "Irgendetwas ist passiert. Wir arbeiten daran!"
#: templates/email/checking/shared_data_checked.html:4
msgid "Shared data checked"
msgstr "Freigegebene Daten geprüft"
#: templates/email/checking/shared_data_checked.html:8
#: templates/email/deleting/shared_data_deleted.html:8
#: templates/email/recording/shared_data_recorded.html:8
#: templates/email/recording/shared_data_unrecorded.html:8
#: templates/email/sharing/shared_access_given.html:8
#: templates/email/sharing/shared_access_removed.html:8
msgid "Hello "
msgstr "Hallo "
#: templates/email/checking/shared_data_checked.html:10
msgid "the following dataset has just been checked"
msgstr "der folgende Datensatz wurde soeben geprüft "
#: templates/email/checking/shared_data_checked.html:14
msgid ""
"This means, the responsible registration office just confirmed the "
"correctness of this dataset."
msgstr ""
"Das bedeutet, dass die zuständige Zulassungsbehörde die Korrektheit des "
"Datensatzes soeben bestätigt hat."
#: templates/email/checking/shared_data_checked.html:17
#: templates/email/deleting/shared_data_deleted.html:17
#: templates/email/recording/shared_data_recorded.html:17
#: templates/email/recording/shared_data_unrecorded.html:17
#: templates/email/sharing/shared_access_given.html:18
#: templates/email/sharing/shared_access_removed.html:18
msgid "Best regards"
msgstr "Beste Grüße"
#: templates/email/deleting/shared_data_deleted.html:4
msgid "Shared data deleted"
msgstr "Freigegebene Daten gelöscht"
#: templates/email/deleting/shared_data_deleted.html:10
msgid "the following dataset has just been deleted"
msgstr "der folgende Datensatz wurde soeben gelöscht "
#: templates/email/deleting/shared_data_deleted.html:14
msgid ""
"If this should not have been happened, please contact us. See the signature "
"for details."
msgstr ""
"Falls das nicht hätte passieren dürfen, kontaktieren Sie uns bitte. In der E-"
"mail Signatur finden Sie weitere Kontaktinformationen."
#: templates/email/recording/shared_data_recorded.html:4
msgid "Shared data recorded"
msgstr "Freigegebene Daten verzeichnet"
#: templates/email/recording/shared_data_recorded.html:10
msgid "the following dataset has just been recorded"
msgstr "der folgende Datensatz wurde soeben verzeichnet "
#: templates/email/recording/shared_data_recorded.html:14
msgid "This means the data is now publicly available, e.g. in LANIS"
msgstr ""
"Das bedeutet, dass die Daten nun öffentlich verfügbar sind, z.B. im LANIS."
#: templates/email/recording/shared_data_recorded.html:24
msgid ""
"Please note: Recorded intervention means the compensations are recorded as "
"well."
msgstr ""
"Bitte beachten Sie: Verzeichnete Eingriffe bedeuten, dass auch die "
"zugehörigen Kompensationen automatisch verzeichnet sind."
#: templates/email/recording/shared_data_unrecorded.html:4
msgid "Shared data unrecorded"
msgstr "Freigegebene Daten entzeichnet"
#: templates/email/recording/shared_data_unrecorded.html:10
msgid "the following dataset has just been unrecorded"
msgstr "der folgende Datensatz wurde soeben entzeichnet "
#: templates/email/recording/shared_data_unrecorded.html:14
msgid "This means the data is no longer publicly available."
msgstr "Das bedeutet, dass die Daten nicht länger öffentlich verfügbar sind."
#: templates/email/recording/shared_data_unrecorded.html:24
msgid ""
"Please note: Unrecorded intervention means the compensations are unrecorded "
"as well."
msgstr ""
"Bitte beachten Sie: Entzeichnete Eingriffe bedeuten, dass auch die "
"zugehörigen Kompensationen automatisch entzeichnet worden sind."
#: templates/email/sharing/shared_access_given.html:4
msgid "Access shared"
msgstr "Zugriff freigegeben"
#: templates/email/sharing/shared_access_given.html:10
msgid "the following dataset has just been shared with you"
msgstr "der folgende Datensatz wurde soeben für Sie freigegeben "
#: templates/email/sharing/shared_access_given.html:14
msgid "This means you can now edit this dataset."
msgstr "Das bedeutet, dass Sie diesen Datensatz nun auch bearbeiten können."
#: templates/email/sharing/shared_access_given.html:15
msgid ""
"The shared dataset appears now by default on your overview for this dataset "
"type."
msgstr ""
"Der freigegebene Datensatz ist nun standardmäßig in Ihrer Übersicht für den "
"Datensatztyp im KSP gelistet."
#: templates/email/sharing/shared_access_given.html:25
msgid ""
"Please note: Shared access on an intervention means you automatically have "
"editing access to related compensations."
msgstr ""
"Bitte beachten Sie: Freigegebener Zugriff auf einen Eingriff bedeutet, dass "
"Sie automatisch auch Zugriff auf die zugehörigen Kompensationen erhalten "
"haben."
#: templates/email/sharing/shared_access_removed.html:4
msgid "Shared access removed"
msgstr "Freigegebener Zugriff entzogen"
#: templates/email/sharing/shared_access_removed.html:10
msgid ""
"your shared access, including editing, has been revoked for the dataset "
msgstr ""
"Ihnen wurde soeben der bearbeitende Zugriff auf den folgenden Datensatz "
"entzogen: "
#: templates/email/sharing/shared_access_removed.html:14
msgid "However, you are still able to view the dataset content."
msgstr "Sie können den Datensatz aber immer noch im KSP einsehen."
#: templates/email/sharing/shared_access_removed.html:15
msgid ""
"Please use the provided search filter on the dataset`s overview pages to "
"find them."
msgstr ""
"Nutzen Sie hierzu einfach die entsprechenden Suchfilter auf den "
"Übersichtsseiten"
#: templates/email/signature.html:6
msgid "Please do not reply on this mail."
msgstr "Bitte antworten Sie nicht auf diese Mail."
#: templates/email/signature.html:8
msgid "If needed, please contact "
msgstr "Bei Rückfragen, wenden Sie sich bitte an "
#: templates/footer.html:6
msgid "Help"
msgstr "Hilfe"
@ -1875,35 +2104,35 @@ msgstr "Abbrechen"
msgid "Fields with * are required."
msgstr "* sind Pflichtfelder."
#: templates/generic_index.html:28
#: templates/generic_index.html:39
msgid "New entry"
msgstr "Neuer Eintrag"
#: templates/generic_index.html:30
#: templates/generic_index.html:41
msgid "New"
msgstr "Neu"
#: templates/generic_index.html:45
#: templates/generic_index.html:56
msgid "Search for keywords"
msgstr "Nach Schlagwörtern suchen"
#: templates/generic_index.html:45
#: templates/generic_index.html:56
msgid "Search"
msgstr "Suchen"
#: templates/generic_index.html:46
#: templates/generic_index.html:57
msgid "Start search"
msgstr "Starte Suche"
#: templates/generic_index.html:58
#: templates/generic_index.html:69
msgid "Results per page"
msgstr "Treffer pro Seite"
#: templates/generic_index.html:82 templates/generic_index.html:88
#: templates/generic_index.html:93 templates/generic_index.html:118
msgid "Filter"
msgstr ""
#: templates/generic_index.html:90
#: templates/generic_index.html:120
msgid "Apply filter"
msgstr "Filter anwenden"
@ -1984,31 +2213,31 @@ msgstr "Wann wollen Sie per E-Mail benachrichtigt werden?"
msgid "Edit notifications"
msgstr "Benachrichtigungen bearbeiten"
#: user/forms.py:76 user/templates/user/index.html:9
#: user/forms.py:72 user/templates/user/index.html:9
msgid "Username"
msgstr "Nutzername"
#: user/forms.py:87
#: user/forms.py:83
msgid "Person name"
msgstr "Name"
#: user/forms.py:98 user/templates/user/index.html:17
#: user/forms.py:94 user/templates/user/index.html:17
msgid "E-Mail"
msgstr ""
#: user/forms.py:112
#: user/forms.py:108
msgid "User contact data"
msgstr "Kontaktdaten"
#: user/models/user_action.py:21
#: user/models/user_action.py:20
msgid "Unrecorded"
msgstr "Entzeichnet"
#: user/models/user_action.py:23
#: user/models/user_action.py:22
msgid "Edited"
msgstr "Bearbeitet"
#: user/models/user_action.py:24
#: user/models/user_action.py:23
msgid "Deleted"
msgstr "Gelöscht"
@ -2053,7 +2282,7 @@ msgstr "Benachrichtigungseinstellungen ändern"
msgid "Notification settings"
msgstr "Benachrichtigungen"
#: user/views.py:56
#: user/views.py:52
msgid "Notifications edited"
msgstr "Benachrichtigungen bearbeitet"
@ -3560,6 +3789,18 @@ msgstr ""
msgid "Unable to connect to qpid with SASL mechanism %s"
msgstr ""
#~ msgid "On registered data edited"
#~ msgstr "Wenn meine freigegebenen Daten bearbeitet wurden"
#~ msgid "Not recorded"
#~ msgstr "Noch nicht verzeichnet"
#~ msgid "On new related data"
#~ msgstr "Wenn neue Daten für mich angelegt werden"
#~ msgid "On disabled share link"
#~ msgstr "Wenn ein Freigabelink deaktiviert wird"
#~ msgid "Deduct"
#~ msgstr "Abbuchen"

13
templates/404.html Normal file
View File

@ -0,0 +1,13 @@
{% extends 'public_base.html' %}
{% load i18n fontawesome_5 %}
{% block body %}
<div class="jumbotron">
<h1 class="display-4">{% fa5_icon 'question-circle' %} 404</h1>
<h1 class="display-4">{% trans 'Not found' %}</h1>
<hr>
<p class="lead">
{% trans 'The requested data does not exist.' %}
</p>
</div>
{% endblock %}

13
templates/500.html Normal file
View File

@ -0,0 +1,13 @@
{% extends 'public_base.html' %}
{% load i18n fontawesome_5 %}
{% block body %}
<div class="jumbotron">
<h1 class="display-4">{% fa5_icon 'fire-extinguisher' %} {% fa5_icon 'fire-alt' %} 500</h1>
<h1 class="display-4">{% trans 'Server Error' %}</h1>
<hr>
<p class="lead">
{% trans 'Something happened. We are working on it!' %}
</p>
</div>
{% endblock %}

View File

@ -0,0 +1,26 @@
{% load i18n %}
<div>
<h2>{% trans 'Shared data checked' %}</h2>
<h4>{{obj_identifier}}</h4>
<hr>
<article>
{% trans 'Hello ' %} {{user.username}},
<br>
{% trans 'the following dataset has just been checked' %}
<br>
<strong>'{{obj_identifier}}'</strong>
<br>
{% trans 'This means, the responsible registration office just confirmed the correctness of this dataset.' %}
<br>
<br>
{% trans 'Best regards' %}
<br>
KSP
<br>
<br>
<br>
{% include 'email/signature.html' %}
</article>
</div>

View File

@ -0,0 +1,26 @@
{% load i18n %}
<div>
<h2>{% trans 'Shared data deleted' %}</h2>
<h4>{{obj_identifier}}</h4>
<hr>
<article>
{% trans 'Hello ' %} {{user.username}},
<br>
{% trans 'the following dataset has just been deleted' %}
<br>
<strong>'{{obj_identifier}}'</strong>
<br>
{% trans 'If this should not have been happened, please contact us. See the signature for details.' %}
<br>
<br>
{% trans 'Best regards' %}
<br>
KSP
<br>
<br>
<br>
{% include 'email/signature.html' %}
</article>
</div>

View File

@ -0,0 +1,31 @@
{% load i18n %}
<div>
<h2>{% trans 'Shared data recorded' %}</h2>
<h4>{{obj_identifier}}</h4>
<hr>
<article>
{% trans 'Hello ' %} {{user.username}},
<br>
{% trans 'the following dataset has just been recorded' %}
<br>
<strong>'{{obj_identifier}}'</strong>
<br>
{% trans 'This means the data is now publicly available, e.g. in LANIS' %}
<br>
<br>
{% trans 'Best regards' %}
<br>
KSP
<br>
<br>
<br>
<small>
{% trans 'Please note: Recorded intervention means the compensations are recorded as well.' %}
</small>
<br>
<br>
{% include 'email/signature.html' %}
</article>
</div>

View File

@ -0,0 +1,31 @@
{% load i18n %}
<div>
<h2>{% trans 'Shared data unrecorded' %}</h2>
<h4>{{obj_identifier}}</h4>
<hr>
<article>
{% trans 'Hello ' %} {{user.username}},
<br>
{% trans 'the following dataset has just been unrecorded' %}
<br>
<strong>'{{obj_identifier}}'</strong>
<br>
{% trans 'This means the data is no longer publicly available.' %}
<br>
<br>
{% trans 'Best regards' %}
<br>
KSP
<br>
<br>
<br>
<small>
{% trans 'Please note: Unrecorded intervention means the compensations are unrecorded as well.' %}
</small>
<br>
<br>
{% include 'email/signature.html' %}
</article>
</div>

View File

@ -0,0 +1,32 @@
{% load i18n %}
<div>
<h2>{% trans 'Access shared' %}</h2>
<h4>{{obj_identifier}}</h4>
<hr>
<article>
{% trans 'Hello ' %} {{user.username}},
<br>
{% trans 'the following dataset has just been shared with you' %}
<br>
<strong>'{{obj_identifier}}'</strong>
<br>
{% trans 'This means you can now edit this dataset.' %}
{% trans 'The shared dataset appears now by default on your overview for this dataset type.' %}
<br>
<br>
{% trans 'Best regards' %}
<br>
KSP
<br>
<br>
<br>
<small>
{% trans 'Please note: Shared access on an intervention means you automatically have editing access to related compensations.' %}
</small>
<br>
<br>
{% include 'email/signature.html' %}
</article>
</div>

View File

@ -0,0 +1,27 @@
{% load i18n %}
<div>
<h2>{% trans 'Shared access removed' %}</h2>
<h4>{{obj_identifier}}</h4>
<hr>
<article>
{% trans 'Hello ' %} {{user.username}},
<br>
{% trans 'your shared access, including editing, has been revoked for the dataset ' %}
<br>
<strong>'{{obj_identifier}}'</strong>
<br>
{% trans 'However, you are still able to view the dataset content.' %}
{% trans 'Please use the provided search filter on the dataset`s overview pages to find them.' %}
<br>
<br>
{% trans 'Best regards' %}
<br>
KSP
<br>
<br>
<br>
{% include 'email/signature.html' %}
</article>
</div>

View File

@ -0,0 +1,10 @@
{% load i18n %}
<div>
<hr>
<small>
{% trans 'Please do not reply on this mail.' %}
<br>
{% trans 'If needed, please contact ' %} <a href="mailto:{{EMAIL_REPLY_TO}}">{{EMAIL_REPLY_TO}}</a>.
</small>
</div>

View File

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

View File

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

View File

@ -9,7 +9,7 @@
<a href="{% url 'home' %}">{% trans 'Impressum' %}</a>
</span>
<span class="col-sm-auto footer-link">
<a href="{% url 'home' %}">{% trans 'Contact' %}</a>
<a href="mailto:{{CONTACT_MAIL}}">{% trans 'Contact' %}</a>
</span>
</div>
</div>

View File

@ -3,6 +3,16 @@
{% load django_tables2 %}
{% load i18n static fontawesome_5 %}
{% block head %}
{% comment %}
dal documentation (django-autocomplete-light) states using form.media for adding needed scripts.
This does not work properly with modal forms, as the scripts are not loaded properly inside the modal.
Therefore the script linkages from form.media have been extracted and put inside dal/scripts.html to ensure
these scripts are loaded when needed.
{% endcomment %}
{% include 'dal/scripts.html' %}
{% endblock %}
{% block body %}
<div class="col-md">
{% if table.title %}
@ -41,7 +51,7 @@
<div class="row my-1">
<div class="col-sm-12 col-md-8 col-lg-6">
<div class="input-group">
<input id="id_{{table.filter.filters.q.field_name}}" name="{{table.filter.filters.q.field_name}}" type="text" class="form-control" aria-label="{% trans 'Search for keywords' %}" placeholder="{% trans 'Search' %}" value="{{ request.GET.q }}">
<input id="id_{{table.filter.query_filter.filters.q.field_name}}" name="{{table.filter.query_filter.filters.q.field_name}}" type="text" class="form-control" aria-label="{% trans 'Search for keywords' %}" placeholder="{% trans 'Search' %}" value="{{ request.GET.q }}">
<div class="input-group-append" title="{% trans 'Start search' %}">
<button type="submit" class="btn btn-default input-group-text">
<span class="">
@ -82,11 +92,32 @@
</div>
<div id="filter" class="collapse" aria-labelledby="filterHeader">
<div class="card-body">
{{ table.filter.form.as_p }}
<button class="btn btn-default" title="{% trans 'Filter' %}">
{% fa5_icon 'filter' %}
{% trans 'Apply filter' %}
</button>
<div class="">
<div class="">
{% with table.filter.selection_filter.form as form %}
{% include 'filter/query_filter.html' %}
{% endwith %}
</div>
<hr>
<div class="mt-3">
{% with table.filter.query_filter.form as form %}
{% include 'filter/query_filter.html' %}
{% endwith %}
</div>
<hr>
<div class="mt-3">
{% with table.filter.checkbox_filter.form as form %}
{% include 'filter/checkbox_filter.html' %}
{% endwith %}
</div>
<hr>
<div class="mt-3">
<button class="btn btn-default" title="{% trans 'Filter' %}">
{% fa5_icon 'filter' %}
{% trans 'Apply filter' %}
</button>
</div>
</div>
</div>
</div>
</div>

View File

@ -41,8 +41,6 @@
</div>
<div class="dropdown-menu">
<a class="dropdown-item" href="{% url 'ema:index' %}" title="{% trans 'Payment funded compensations' %}">{% fa5_icon 'euro-sign' %} {% trans 'EMA' %}</a>
<a class="dropdown-item" href="{% url 'home' %}">{% fa5_icon 'file-import' %} {% trans 'Import...' %}</a>
<a class="dropdown-item" href="{% url 'home' %}">{% fa5_icon 'file-export' %} {% trans 'Export...' %}</a>
<a class="dropdown-item" href="{% url 'analysis:reports' %}">{% fa5_icon 'file-alt' %} {% trans 'Reports' %}</a>
</div>
</li>

View File

@ -1,6 +1,6 @@
from django.contrib import admin
from user.models import UserNotification, KonovaUserExtension, UserActionLogEntry
from user.models import UserNotification, UserActionLogEntry, User
class UserNotificationAdmin(admin.ModelAdmin):
@ -11,9 +11,13 @@ class UserNotificationAdmin(admin.ModelAdmin):
]
class KonovaUserExtensionAdmin(admin.ModelAdmin):
class UserAdmin(admin.ModelAdmin):
list_display = [
"user",
"id",
"username",
"first_name",
"last_name",
"email",
]
@ -27,5 +31,5 @@ class UserActionLogEntryAdmin(admin.ModelAdmin):
admin.site.register(UserNotification, UserNotificationAdmin)
admin.site.register(KonovaUserExtension, KonovaUserExtensionAdmin)
admin.site.register(UserActionLogEntry, UserActionLogEntryAdmin)
admin.site.register(UserActionLogEntry, UserActionLogEntryAdmin)
admin.site.register(User, UserAdmin)

View File

@ -9,9 +9,8 @@ from konova.enums import BaseEnum
class UserNotificationEnum(BaseEnum):
NOTIFY_ON_NEW_RELATED_DATA = "NOTIFY_ON_NEW_RELATED_DATA" # notifies in case new data has been added which is related to the user's organisation
NOTIFY_ON_SHARE_LINK_DISABLED = "NOTIFY_ON_SHARE_LINK_DISABLED" # notifies in case share link for data has been disabled
NOTIFY_ON_SHARED_ACCESS_REMOVED = "NOTIFY_ON_SHARED_ACCESS_REMOVED" # notifies in case shared access to data has been removed
NOTIFY_ON_SHARED_DATA_RECORDED = "NOTIFY_ON_SHARED_DATA_RECORDED" # notifies in case data has been "verzeichnet"
NOTIFY_ON_SHARED_DATA_DELETED = "NOTIFY_ON_SHARED_DATA_DELETED" # notifies in case data has been deleted
NOTIFY_ON_REGISTERED_DATA_EDITED = "NOTIFY_ON_REGISTERED_DATA_EDITED" # notifies in case registered ("verzeichnet") data has been edited
NOTIFY_ON_SHARED_DATA_CHECKED = "NOTIFY_ON_SHARED_DATA_CHECKED" # notifies in case shared data has been checked
NOTIFY_ON_SHARED_ACCESS_GAINED = "NOTIFY_ON_SHARED_ACCESS_GAINED" # notifies in case new access has been gained

View File

@ -8,10 +8,10 @@ Created on: 08.07.21
from django import forms
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import User
from user.models import User
from konova.forms import BaseForm, BaseModalForm
from user.models import UserNotification, KonovaUserExtension
from user.models import UserNotification
class UserNotificationForm(BaseForm):
@ -50,11 +50,7 @@ class UserNotificationForm(BaseForm):
)
self.fields["notifications"].choices = choices
# Set currently selected notifications as initial
self.konova_extension = KonovaUserExtension.objects.get_or_create(
user=user
)[0]
users_current_notifications = self.konova_extension.notifications.all()
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
@ -68,7 +64,7 @@ class UserNotificationForm(BaseForm):
notifications = UserNotification.objects.filter(
id__in=selected_notification_ids,
)
self.konova_extension.notifications.set(notifications)
self.user.notifications.set(notifications)
class UserContactForm(BaseModalForm):

View File

@ -6,5 +6,5 @@ Created on: 15.11.21
"""
from .user_action import *
from .konova_user import *
from .user import *
from .notification import *

View File

@ -1,19 +0,0 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 15.11.21
"""
from django.contrib.auth.models import User
from django.db import models
class KonovaUserExtension(models.Model):
""" Extension model for additional ksp features
Extends the default user model for some extras
"""
user = models.OneToOneField(User, on_delete=models.CASCADE)
notifications = models.ManyToManyField("user.UserNotification", related_name="+")

106
user/models/user.py Normal file
View File

@ -0,0 +1,106 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 15.11.21
"""
from django.contrib.auth.models import AbstractUser
from django.db import models
from konova.utils.mailer import Mailer
from user.enums import UserNotificationEnum
class User(AbstractUser):
notifications = models.ManyToManyField("user.UserNotification", related_name="+", blank=True)
def is_notification_setting_set(self, notification_enum: UserNotificationEnum):
return self.notifications.filter(
id=notification_enum.value
).exists()
def send_mail_shared_access_removed(self, obj_identifier):
""" Sends a mail to the user in case of removed shared access
Args:
obj_identifier ():
Returns:
"""
notification_set = self.is_notification_setting_set(UserNotificationEnum.NOTIFY_ON_SHARED_ACCESS_REMOVED)
if notification_set:
mailer = Mailer()
mailer.send_mail_shared_access_removed(obj_identifier, self)
def send_mail_shared_access_given(self, obj_identifier):
""" Sends a mail to the user in case of given shared access
Args:
obj_identifier ():
Returns:
"""
notification_set = self.is_notification_setting_set(UserNotificationEnum.NOTIFY_ON_SHARED_ACCESS_GAINED)
if notification_set:
mailer = Mailer()
mailer.send_mail_shared_access_given(obj_identifier, self)
def send_mail_shared_data_recorded(self, obj_identifier):
""" Sends a mail to the user in case of shared data has been recorded
Args:
obj_identifier ():
Returns:
"""
notification_set = self.is_notification_setting_set(UserNotificationEnum.NOTIFY_ON_SHARED_DATA_RECORDED)
if notification_set:
mailer = Mailer()
mailer.send_mail_shared_data_recorded(obj_identifier, self)
def send_mail_shared_data_unrecorded(self, obj_identifier):
""" Sends a mail to the user in case of shared data has been unrecorded
Args:
obj_identifier ():
Returns:
"""
notification_set = self.is_notification_setting_set(UserNotificationEnum.NOTIFY_ON_SHARED_DATA_RECORDED)
if notification_set:
mailer = Mailer()
mailer.send_mail_shared_data_unrecorded(obj_identifier, self)
def send_mail_shared_data_deleted(self, obj_identifier):
""" Sends a mail to the user in case of shared data has been deleted
Args:
obj_identifier ():
Returns:
"""
notification_set = self.is_notification_setting_set(UserNotificationEnum.NOTIFY_ON_SHARED_DATA_DELETED)
if notification_set:
mailer = Mailer()
mailer.send_mail_shared_data_deleted(obj_identifier, self)
def send_mail_shared_data_checked(self, obj_identifier):
""" Sends a mail to the user in case of shared data has been deleted
Args:
obj_identifier ():
Returns:
"""
notification_set = self.is_notification_setting_set(UserNotificationEnum.NOTIFY_ON_SHARED_DATA_CHECKED)
if notification_set:
mailer = Mailer()
mailer.send_mail_shared_data_checked(obj_identifier, self)

View File

@ -7,7 +7,6 @@ Created on: 15.11.21
"""
import uuid
from django.contrib.auth.models import User
from django.db import models
from django.utils.translation import gettext_lazy as _
@ -34,7 +33,7 @@ class UserActionLogEntry(models.Model):
primary_key=True,
default=uuid.uuid4,
)
user = models.ForeignKey(User, related_name='+', on_delete=models.CASCADE, help_text="Performing user")
user = models.ForeignKey("user.User", related_name='+', on_delete=models.CASCADE, help_text="Performing user")
timestamp = models.DateTimeField(auto_now_add=True, help_text="Timestamp of performed action")
action = models.CharField(
max_length=255,
@ -50,9 +49,6 @@ class UserActionLogEntry(models.Model):
"-timestamp",
)
def __str__(self):
return "{} | {} | {}".format(self.user.username, self.timestamp, self.action)
@property
def action_humanize(self):
""" Returns humanized version of enum
@ -69,7 +65,7 @@ class UserActionLogEntry(models.Model):
return None
@classmethod
def get_created_action(cls, user: User, comment: str = None):
def get_created_action(cls, user, comment: str = None):
action = UserActionLogEntry.objects.create(
user=user,
action=UserAction.CREATED,
@ -78,7 +74,7 @@ class UserActionLogEntry(models.Model):
return action
@classmethod
def get_edited_action(cls, user: User, comment: str = None):
def get_edited_action(cls, user, comment: str = None):
action = UserActionLogEntry.objects.create(
user=user,
action=UserAction.EDITED,
@ -87,7 +83,7 @@ class UserActionLogEntry(models.Model):
return action
@classmethod
def get_deleted_action(cls, user: User, comment: str = None):
def get_deleted_action(cls, user, comment: str = None):
action = UserActionLogEntry.objects.create(
user=user,
action=UserAction.DELETED,
@ -96,7 +92,7 @@ class UserActionLogEntry(models.Model):
return action
@classmethod
def get_checked_action(cls, user: User, comment: str = None):
def get_checked_action(cls, user, comment: str = None):
action = UserActionLogEntry.objects.create(
user=user,
action=UserAction.CHECKED,
@ -105,7 +101,7 @@ class UserActionLogEntry(models.Model):
return action
@classmethod
def get_recorded_action(cls, user: User, comment: str = None):
def get_recorded_action(cls, user, comment: str = None):
action = UserActionLogEntry.objects.create(
user=user,
action=UserAction.RECORDED,
@ -114,7 +110,7 @@ class UserActionLogEntry(models.Model):
return action
@classmethod
def get_unrecorded_action(cls, user: User, comment: str = None):
def get_unrecorded_action(cls, user, comment: str = None):
action = UserActionLogEntry.objects.create(
user=user,
action=UserAction.UNRECORDED,

View File

@ -1,6 +1,6 @@
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from user.models import User
from django.http import HttpRequest
from django.shortcuts import render, redirect, get_object_or_404
from django.utils.translation import gettext_lazy as _
@ -8,7 +8,6 @@ from django.utils.translation import gettext_lazy as _
from konova.contexts import BaseContext
from konova.decorators import any_group_check
from user.forms import UserNotificationForm, UserContactForm
from user.models import KonovaUserExtension
@login_required
@ -43,9 +42,6 @@ def notifications_view(request: HttpRequest):
"""
template = "user/notifications.html"
user = request.user
konova_ext = KonovaUserExtension.objects.get_or_create(
user=user
)[0]
form = UserNotificationForm(user=user, data=request.POST or None)
if request.method == "POST":
@ -65,7 +61,6 @@ def notifications_view(request: HttpRequest):
context = {
"user": user,
"form": form,
"konova_ext": konova_ext,
}
context = BaseContext(request, context).context
return render(request, template, context)