Initial
This commit is contained in:
0
kspneo/__init__.py
Normal file
0
kspneo/__init__.py
Normal file
32
kspneo/admin.py
Normal file
32
kspneo/admin.py
Normal file
@@ -0,0 +1,32 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||
Created on: 07.12.20
|
||||
|
||||
"""
|
||||
from django.contrib import admin
|
||||
|
||||
from konova.models import RoleType, RoleGroup
|
||||
|
||||
|
||||
class RoleTypeAdmin(admin.ModelAdmin):
|
||||
list_display = [
|
||||
"type",
|
||||
"created_on",
|
||||
"created_by",
|
||||
]
|
||||
|
||||
|
||||
class RoleGroupAdmin(admin.ModelAdmin):
|
||||
list_display = [
|
||||
"name",
|
||||
"organisation",
|
||||
"role",
|
||||
]
|
||||
|
||||
|
||||
admin.site.register(RoleType, RoleTypeAdmin)
|
||||
admin.site.register(RoleGroup, RoleGroupAdmin)
|
||||
|
||||
|
||||
16
kspneo/asgi.py
Normal file
16
kspneo/asgi.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
ASGI config for konova project.
|
||||
|
||||
It exposes the ASGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/3.1/howto/deployment/asgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.asgi import get_asgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'konova.settings')
|
||||
|
||||
application = get_asgi_application()
|
||||
42
kspneo/autocompletes.py
Normal file
42
kspneo/autocompletes.py
Normal file
@@ -0,0 +1,42 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||
Created on: 07.12.20
|
||||
|
||||
"""
|
||||
from dal_select2.views import Select2QuerySetView
|
||||
|
||||
from organisation.enums import OrganisationTypeEnum
|
||||
from organisation.models import Organisation
|
||||
|
||||
|
||||
class OrganisationAutocomplete(Select2QuerySetView):
|
||||
def get_queryset(self):
|
||||
if self.request.user.is_anonymous:
|
||||
return Organisation.objects.none()
|
||||
qs = Organisation.objects.all()
|
||||
if self.q:
|
||||
qs = qs.filter(name__icontains=self.q)
|
||||
qs = qs.order_by(
|
||||
"name"
|
||||
)
|
||||
return qs
|
||||
|
||||
|
||||
class NonOfficialOrganisationAutocomplete(Select2QuerySetView):
|
||||
def get_queryset(self):
|
||||
if self.request.user.is_anonymous:
|
||||
return Organisation.objects.none()
|
||||
qs = Organisation.objects.all()
|
||||
if self.q:
|
||||
qs = qs.filter(
|
||||
name__icontains=self.q,
|
||||
)
|
||||
qs = qs.exclude(
|
||||
type=OrganisationTypeEnum.OFFICIAL.value
|
||||
)
|
||||
qs = qs.order_by(
|
||||
"name"
|
||||
)
|
||||
return qs
|
||||
53
kspneo/contexts.py
Normal file
53
kspneo/contexts.py
Normal file
@@ -0,0 +1,53 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||
Created on: 16.11.20
|
||||
|
||||
"""
|
||||
from django.http import HttpRequest
|
||||
|
||||
from konova.models import RoleGroup
|
||||
from konova.sub_settings.context_settings import BASE_TITLE, WIKI_URL, BASE_FRONTEND_TITLE, RENDER_HEADER
|
||||
from konova.utils.session import set_session_user_role
|
||||
|
||||
|
||||
class BaseContext:
|
||||
"""
|
||||
Holds all base data which is needed for every context rendering
|
||||
"""
|
||||
context = {
|
||||
"base_title": BASE_TITLE,
|
||||
"base_frontend_title": BASE_FRONTEND_TITLE,
|
||||
"language": "en",
|
||||
"wiki_url": WIKI_URL,
|
||||
"user": None,
|
||||
"render_header": RENDER_HEADER,
|
||||
"current_role": None,
|
||||
}
|
||||
|
||||
def __init__(self, request: HttpRequest, additional_context: dict = {}):
|
||||
self.context["language"] = request.LANGUAGE_CODE
|
||||
self.context["user"] = request.user
|
||||
self.__handle_current_role(request)
|
||||
|
||||
# Add additional context, derived from given parameters
|
||||
self.context.update(additional_context)
|
||||
|
||||
def __handle_current_role(self, request: HttpRequest):
|
||||
""" Reads/Writes current role from/to session
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
# Store current role in session object to reduce amount of db access
|
||||
current_role = request.session.get("current_role", {})
|
||||
|
||||
if len(current_role) == 0 and request.user.is_authenticated:
|
||||
role_group = RoleGroup.get_users_role_groups(request.user).first()
|
||||
current_role = set_session_user_role(request, role_group)
|
||||
|
||||
self.context["current_role"] = current_role
|
||||
94
kspneo/decorators.py
Normal file
94
kspneo/decorators.py
Normal file
@@ -0,0 +1,94 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||
Created on: 16.11.20
|
||||
|
||||
"""
|
||||
|
||||
from functools import wraps
|
||||
|
||||
from django.contrib import messages
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.shortcuts import redirect
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from konova.models import RoleGroup
|
||||
from konova.utils.session import get_session_user_role
|
||||
from organisation.enums import RoleTypeEnum
|
||||
from process.enums import PROCESS_EDITABLE_STATE
|
||||
from process.models import Process
|
||||
|
||||
|
||||
def staff_required(function):
|
||||
"""
|
||||
A decorator for functions which shall only be usable for staff members of the system
|
||||
"""
|
||||
@wraps(function)
|
||||
def wrap(request, *args, **kwargs):
|
||||
user = request.user
|
||||
if user.is_staff:
|
||||
return function(request, *args, **kwargs)
|
||||
else:
|
||||
messages.info(request, _("You need to be staff to perform this action!"))
|
||||
return redirect(request.META.get("HTTP_REFERER", reverse("home")))
|
||||
return wrap
|
||||
|
||||
|
||||
def superuser_required(function):
|
||||
"""
|
||||
A decorator for functions which shall only be usable for superusers of the system
|
||||
"""
|
||||
@wraps(function)
|
||||
def wrap(request, *args, **kwargs):
|
||||
user = request.user
|
||||
if user.is_superuser:
|
||||
return function(request, *args, **kwargs)
|
||||
else:
|
||||
messages.info(request, _("You need to be administrator to perform this action!"))
|
||||
return redirect(request.META.get("HTTP_REFERER", reverse("home")))
|
||||
return wrap
|
||||
|
||||
|
||||
def resolve_user_role(function):
|
||||
"""
|
||||
A decorator for functions to resolve the current user role and store it in the user object
|
||||
"""
|
||||
@wraps(function)
|
||||
def wrap(request, *args, **kwargs):
|
||||
user = request.user
|
||||
role = get_session_user_role(request)
|
||||
try:
|
||||
role = RoleGroup.objects.get(id=role.get("id", -1))
|
||||
user.current_role = role
|
||||
except ObjectDoesNotExist:
|
||||
user.current_role = None
|
||||
return function(request, *args, **kwargs)
|
||||
return wrap
|
||||
|
||||
|
||||
def valid_process_role_required(function):
|
||||
"""
|
||||
A decorator for functions to check whether the user has a valid role selected
|
||||
"""
|
||||
@wraps(function)
|
||||
def wrap(request, *args, **kwargs):
|
||||
user = request.user
|
||||
if user.current_role is None:
|
||||
role = get_session_user_role(request)
|
||||
else:
|
||||
role = user.current_role
|
||||
try:
|
||||
process = Process.objects.get(id=kwargs.get("id"))
|
||||
editable = PROCESS_EDITABLE_STATE.get(process.state)
|
||||
role_enum = RoleTypeEnum[role.role.type]
|
||||
if role_enum in editable:
|
||||
return function(request, *args, **kwargs)
|
||||
else:
|
||||
messages.error(request, _("Your current role is not allowed to do this"))
|
||||
return redirect(request.META.get("HTTP_REFERER", "home"))
|
||||
except ObjectDoesNotExist:
|
||||
process = None
|
||||
return function(request, *args, **kwargs)
|
||||
return wrap
|
||||
40
kspneo/enums.py
Normal file
40
kspneo/enums.py
Normal file
@@ -0,0 +1,40 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||
Created on: 17.11.20
|
||||
|
||||
"""
|
||||
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class BaseEnum(Enum):
|
||||
""" Provides basic functionality for Enums
|
||||
|
||||
"""
|
||||
@classmethod
|
||||
def as_choices(cls, drop_empty_choice: bool = False):
|
||||
empty_choice = [] if drop_empty_choice else [(None, "---")]
|
||||
choices = empty_choice + [(enum.value, enum.name) for enum in cls]
|
||||
return choices
|
||||
|
||||
|
||||
class UnitEnum(BaseEnum):
|
||||
"""
|
||||
Predefines units for selection
|
||||
"""
|
||||
mm = "mm"
|
||||
dm = "dm"
|
||||
cm = "cm"
|
||||
m = "m"
|
||||
km = "km"
|
||||
|
||||
qmm = "qmm"
|
||||
qdm = "qdm"
|
||||
qcm = "qcm"
|
||||
qm = "qm"
|
||||
qkm = "qkm"
|
||||
ha = "ha"
|
||||
|
||||
st = "St." # pieces
|
||||
141
kspneo/forms.py
Normal file
141
kspneo/forms.py
Normal file
@@ -0,0 +1,141 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||
Created on: 16.11.20
|
||||
|
||||
"""
|
||||
|
||||
from abc import abstractmethod
|
||||
|
||||
from django import forms
|
||||
from django.http import HttpRequest
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from konova.models import RoleGroup
|
||||
from konova.utils.session import set_session_user_role
|
||||
from organisation.settings import ROLE_TYPE_STRINGS
|
||||
|
||||
|
||||
class BaseForm(forms.Form):
|
||||
"""
|
||||
Basic form for that holds attributes needed in all other forms
|
||||
"""
|
||||
action_url = None
|
||||
form_title = None
|
||||
cancel_redirect = None
|
||||
form_caption = None
|
||||
instance = None # The data holding model object
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.instance = kwargs.pop("instance", None)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@abstractmethod
|
||||
def save(self):
|
||||
# To be implemented in subclasses!
|
||||
pass
|
||||
|
||||
def disable_form_field(self, field: str):
|
||||
"""
|
||||
Disables a form field for user editing
|
||||
"""
|
||||
self.fields[field].widget.attrs["readonly"] = True
|
||||
self.fields[field].disabled = True
|
||||
self.fields[field].widget.attrs["title"] = _("Not editable")
|
||||
|
||||
def initialize_form_field(self, field: str, val):
|
||||
"""
|
||||
Initializes a form field with a value
|
||||
"""
|
||||
self.fields[field].initial = val
|
||||
|
||||
def load_initial_data(self, form_data: dict, disabled_fields: list):
|
||||
""" Initializes form data from instance
|
||||
|
||||
Inserts instance data into form and disables form fields
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
if self.instance is None:
|
||||
return
|
||||
for k, v in form_data.items():
|
||||
self.initialize_form_field(k, v)
|
||||
for field in disabled_fields:
|
||||
self.disable_form_field(field)
|
||||
|
||||
|
||||
class RemoveForm(BaseForm):
|
||||
check = forms.BooleanField(
|
||||
label=_("Confirm"),
|
||||
label_suffix=_(""),
|
||||
required=True,
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.object_to_remove = kwargs.pop("object_to_remove", None)
|
||||
self.remove_post_url = kwargs.pop("remove_post_url", "")
|
||||
self.cancel_url = kwargs.pop("cancel_url", "")
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.form_title = _("Remove")
|
||||
if self.object_to_remove is not None:
|
||||
self.form_caption = _("You are about to remove {} {}").format(self.object_to_remove.__class__.__name__, self.object_to_remove)
|
||||
self.action_url = self.remove_post_url
|
||||
self.cancel_redirect = self.cancel_url
|
||||
|
||||
def is_checked(self) -> bool:
|
||||
return self.cleaned_data.get("check", False)
|
||||
|
||||
def save(self):
|
||||
if self.object_to_remove is not None and self.is_checked():
|
||||
self.object_to_remove.is_active = False
|
||||
self.object_to_remove.is_deleted = True
|
||||
self.object_to_remove.save()
|
||||
return self.object_to_remove
|
||||
|
||||
|
||||
class ChangeUserRoleForm(BaseForm):
|
||||
"""
|
||||
Form for a user to change the current role
|
||||
"""
|
||||
role = forms.ChoiceField(
|
||||
label=_("You are working as"),
|
||||
label_suffix="",
|
||||
choices=[],
|
||||
widget=forms.Select(
|
||||
attrs={
|
||||
"onchange": "submit();",
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
user = kwargs.pop("user", None)
|
||||
super().__init__(*args, **kwargs)
|
||||
self.action_url = reverse("home")
|
||||
self.cancel_redirect = reverse("home")
|
||||
|
||||
role_groups = RoleGroup.get_users_role_groups(user)
|
||||
choices = []
|
||||
for group in role_groups:
|
||||
choices.append(
|
||||
(group.id, "{} ({})".format(ROLE_TYPE_STRINGS.get(group.role.type, None), group.organisation))
|
||||
)
|
||||
self.fields["role"].choices = choices
|
||||
|
||||
def save(self, request: HttpRequest) -> RoleGroup:
|
||||
""" Custom save method for storing the newly selected role
|
||||
|
||||
Args:
|
||||
request (HttpRequest):
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
role_group = RoleGroup.get_users_role_groups(request.user).get(id=self.cleaned_data.get("role", -1))
|
||||
set_session_user_role(request, role_group)
|
||||
return role_group
|
||||
153
kspneo/management/commands/setup.py
Normal file
153
kspneo/management/commands/setup.py
Normal file
@@ -0,0 +1,153 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||
Created on: 15.12.20
|
||||
|
||||
"""
|
||||
from getpass import getpass
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.management import BaseCommand
|
||||
from django.db import transaction
|
||||
|
||||
from konova.management.commands.setup_test_data import TEST_ORGANISATION_DATA, TEST_ROLE_GROUPS_DATA
|
||||
from konova.models import RoleType, RoleGroup
|
||||
from organisation.enums import RoleTypeEnum
|
||||
from organisation.models import Organisation
|
||||
|
||||
CREATED_TEMPLATE = "{} created"
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Initializes database with basic data"
|
||||
|
||||
def handle(self, *args, **options):
|
||||
try:
|
||||
with transaction.atomic():
|
||||
self.__init_superuser()
|
||||
self.__init_test_organisation()
|
||||
self.__init_role_types()
|
||||
self.__init_role_groups()
|
||||
except KeyboardInterrupt:
|
||||
self.__break_line()
|
||||
exit(-1)
|
||||
|
||||
def __init_superuser(self):
|
||||
""" Create a superuser by user prompt input
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
self.stdout.write(
|
||||
self.style.WARNING(
|
||||
"--- Superuser ---",
|
||||
)
|
||||
)
|
||||
username = input("Superuser name: ")
|
||||
if User.objects.filter(username=username).exists():
|
||||
self.stdout.write(
|
||||
self.style.ERROR(
|
||||
"Name already taken!"
|
||||
)
|
||||
)
|
||||
exit(-1)
|
||||
pw = getpass("Password: ")
|
||||
pw_confirm = getpass("Confirm password : ")
|
||||
if pw != pw_confirm:
|
||||
self.stdout.write(
|
||||
self.style.ERROR(
|
||||
"Passwords did not match!"
|
||||
)
|
||||
)
|
||||
exit(-1)
|
||||
|
||||
# Create superuser
|
||||
superuser = User()
|
||||
superuser.username = username
|
||||
superuser.is_superuser = True
|
||||
superuser.is_staff = True
|
||||
superuser.set_password(pw)
|
||||
superuser.save()
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(
|
||||
"Superuser {} created".format(username)
|
||||
)
|
||||
)
|
||||
self.__break_line()
|
||||
|
||||
def __init_role_types(self):
|
||||
""" Initializes available role types according to RoleTypeEnum
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
self.stdout.write(
|
||||
self.style.WARNING(
|
||||
"--- Role types ---"
|
||||
)
|
||||
)
|
||||
for role_type_enum in RoleTypeEnum:
|
||||
role_type = RoleType.objects.get_or_create(
|
||||
type=role_type_enum.value
|
||||
)[0]
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(
|
||||
CREATED_TEMPLATE.format(role_type.type)
|
||||
)
|
||||
)
|
||||
self.__break_line()
|
||||
|
||||
def __init_test_organisation(self):
|
||||
""" Creates test organisations from predefined data
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
self.stdout.write(
|
||||
self.style.WARNING(
|
||||
"--- Organisations ---"
|
||||
)
|
||||
)
|
||||
for org in TEST_ORGANISATION_DATA:
|
||||
db_org = Organisation.objects.get_or_create(
|
||||
**org
|
||||
)[0]
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(
|
||||
CREATED_TEMPLATE.format(db_org.name)
|
||||
)
|
||||
)
|
||||
self.__break_line()
|
||||
|
||||
def __init_role_groups(self):
|
||||
""" Creates test role groups from predefined data
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
self.stdout.write(
|
||||
self.style.WARNING(
|
||||
"--- Role Groups ---"
|
||||
)
|
||||
)
|
||||
for group_data in TEST_ROLE_GROUPS_DATA:
|
||||
group_data["organisation"] = Organisation.objects.get(name=group_data["organisation"])
|
||||
group_data["role"] = RoleType.objects.get(type=group_data["role"])
|
||||
group = RoleGroup.objects.get_or_create(
|
||||
**group_data
|
||||
)[0]
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(
|
||||
CREATED_TEMPLATE.format(group.name)
|
||||
)
|
||||
)
|
||||
self.__break_line()
|
||||
|
||||
def __break_line(self):
|
||||
""" Simply prints a line break
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
self.stdout.write("\n")
|
||||
58
kspneo/management/commands/setup_test_data.py
Normal file
58
kspneo/management/commands/setup_test_data.py
Normal file
@@ -0,0 +1,58 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||
Created on: 15.12.20
|
||||
|
||||
"""
|
||||
from organisation.enums import OrganisationTypeEnum, RoleTypeEnum
|
||||
|
||||
TEST_ORGANISATION_DATA = [
|
||||
{
|
||||
"name": "Test_Official_1",
|
||||
"is_active": True,
|
||||
"is_deleted": False,
|
||||
"type": OrganisationTypeEnum.OFFICIAL.value,
|
||||
},
|
||||
{
|
||||
"name": "Test_Official_2",
|
||||
"is_active": True,
|
||||
"is_deleted": False,
|
||||
"type": OrganisationTypeEnum.OFFICIAL.value,
|
||||
},
|
||||
{
|
||||
"name": "Test_NGO_1",
|
||||
"is_active": True,
|
||||
"is_deleted": False,
|
||||
"type": OrganisationTypeEnum.NGO.value,
|
||||
},
|
||||
{
|
||||
"name": "Test_Company_1",
|
||||
"is_active": True,
|
||||
"is_deleted": False,
|
||||
"type": OrganisationTypeEnum.COMPANY.value,
|
||||
},
|
||||
]
|
||||
|
||||
TEST_ROLE_GROUPS_DATA = [
|
||||
{
|
||||
"name": "Registration office Test_Official_1",
|
||||
"organisation": "Test_Official_1",
|
||||
"role": RoleTypeEnum.REGISTRATIONOFFICE.value,
|
||||
},
|
||||
{
|
||||
"name": "Licensing authority Test_Official_1",
|
||||
"organisation": "Test_Official_1",
|
||||
"role": RoleTypeEnum.LICENSINGAUTHORITY.value,
|
||||
},
|
||||
{
|
||||
"name": "Dataprovider Test_Official_2",
|
||||
"organisation": "Test_Official_2",
|
||||
"role": RoleTypeEnum.LICENSINGAUTHORITY.value,
|
||||
},
|
||||
{
|
||||
"name": "Dataprovider Test_Company_1",
|
||||
"organisation": "Test_Company_1",
|
||||
"role": RoleTypeEnum.DATAPROVIDER.value,
|
||||
},
|
||||
]
|
||||
104
kspneo/models.py
Normal file
104
kspneo/models.py
Normal file
@@ -0,0 +1,104 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||
Created on: 17.11.20
|
||||
|
||||
"""
|
||||
import uuid
|
||||
|
||||
from django.contrib.auth.models import User, Group
|
||||
from django.db import models
|
||||
from django.db.models import QuerySet
|
||||
|
||||
from organisation.enums import RoleTypeEnum
|
||||
|
||||
|
||||
class BaseResource(models.Model):
|
||||
"""
|
||||
A basic resource model, which defines attributes for every derived model
|
||||
"""
|
||||
id = models.UUIDField(
|
||||
primary_key=True,
|
||||
default=uuid.uuid4,
|
||||
)
|
||||
is_active = models.BooleanField(default=True)
|
||||
is_deleted = models.BooleanField(default=False)
|
||||
created_on = models.DateTimeField(auto_now_add=True, null=True)
|
||||
created_by = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
||||
class BaseObject(BaseResource):
|
||||
"""
|
||||
A basic object model, which specifies BaseResource.
|
||||
|
||||
Mainly used for intervention, compensation, ecoaccount
|
||||
"""
|
||||
identifier = models.CharField(max_length=1000, null=True, blank=True)
|
||||
title = models.CharField(max_length=1000, null=True, blank=True)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
||||
class Deadline(BaseResource):
|
||||
"""
|
||||
Defines a deadline, which can be used to define dates with a semantic meaning
|
||||
"""
|
||||
type = models.CharField(max_length=500, null=True, blank=True)
|
||||
date = models.DateField(null=True, blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.type
|
||||
|
||||
|
||||
class Document(BaseResource):
|
||||
"""
|
||||
Documents can be attached to process, compensation or intervention for uploading legal documents or pictures.
|
||||
"""
|
||||
date_of_creation = models.DateField()
|
||||
document = models.FileField()
|
||||
comment = models.TextField()
|
||||
|
||||
|
||||
class RoleType(BaseResource):
|
||||
"""
|
||||
Defines different role types
|
||||
"""
|
||||
type = models.CharField(max_length=255, choices=RoleTypeEnum.as_choices(drop_empty_choice=True), unique=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.type
|
||||
|
||||
|
||||
class RoleGroup(Group):
|
||||
"""
|
||||
Role groups are specialized groups which hold information on which users are related to a certain organisation and
|
||||
a role
|
||||
"""
|
||||
organisation = models.ForeignKey("organisation.Organisation", on_delete=models.CASCADE)
|
||||
role = models.ForeignKey(RoleType, on_delete=models.CASCADE)
|
||||
|
||||
class Meta:
|
||||
unique_together = [
|
||||
["organisation", "role", ]
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def get_users_role_groups(user: User) -> QuerySet:
|
||||
""" Get all role groups of a given user
|
||||
|
||||
Args:
|
||||
user (User): The user
|
||||
|
||||
Returns:
|
||||
qs (QuerySet)
|
||||
"""
|
||||
if user.is_anonymous:
|
||||
return RoleGroup.objects.none()
|
||||
return RoleGroup.objects.filter(
|
||||
user=user
|
||||
)
|
||||
50
kspneo/settings.py
Normal file
50
kspneo/settings.py
Normal file
@@ -0,0 +1,50 @@
|
||||
"""
|
||||
Django settings for konova project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 3.1.2.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/3.1/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/3.1/ref/settings/
|
||||
"""
|
||||
|
||||
# Load other settings
|
||||
from konova.sub_settings.django_settings import *
|
||||
|
||||
# Num of days if user enables Remember-me on login
|
||||
KEEP_LOGGED_DURATION = 30
|
||||
|
||||
# German DateTime string format
|
||||
STRF_DATE_TIME = "%d.%m.%Y %H:%M:%S"
|
||||
|
||||
# Tables
|
||||
RESULTS_PER_PAGE_PARAM = "rpp"
|
||||
PAGE_PARAM = "page"
|
||||
PAGE_SIZE_OPTIONS = [5, 10, 15, 20, 25, 30, 50, 75, 100]
|
||||
PAGE_SIZE_OPTIONS_TUPLES = [
|
||||
(5, 5),
|
||||
(10, 10),
|
||||
(15, 15),
|
||||
(20, 20),
|
||||
(25, 25),
|
||||
(30, 30),
|
||||
(50, 50),
|
||||
(75, 75),
|
||||
(100, 100),
|
||||
]
|
||||
PAGE_SIZE_DEFAULT = 5
|
||||
PAGE_SIZE_MAX = 100
|
||||
PAGE_DEFAULT = 1
|
||||
|
||||
# SSO settings
|
||||
SSO_SERVER_BASE = "http://127.0.0.1:8000/"
|
||||
SSO_SERVER = "{}sso/".format(SSO_SERVER_BASE)
|
||||
SSO_PRIVATE_KEY = "CHANGE_ME"
|
||||
SSO_PUBLIC_KEY = "CHANGE_ME"
|
||||
|
||||
# MAPS
|
||||
DEFAULT_LAT = 50.00
|
||||
DEFAULT_LON = 7.00
|
||||
DEFAULT_ZOOM = 8.0
|
||||
57
kspneo/static/css/kspneo.css
Normal file
57
kspneo/static/css/kspneo.css
Normal file
@@ -0,0 +1,57 @@
|
||||
.body-content{
|
||||
min-height: 75vh;
|
||||
}
|
||||
|
||||
.note{
|
||||
font-size: 0.75rem;
|
||||
color: lightgray;
|
||||
}
|
||||
|
||||
.label-required{
|
||||
color: red;
|
||||
}
|
||||
|
||||
table{
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.footer{
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.error{
|
||||
color: #D8000C !important;
|
||||
background-color: #FFBABA !important;
|
||||
}
|
||||
|
||||
i.true{
|
||||
color: green;
|
||||
}
|
||||
i.false{
|
||||
color: red;
|
||||
}
|
||||
|
||||
.button{
|
||||
margin: 0.5625rem;
|
||||
}
|
||||
|
||||
.action-col{
|
||||
max-width: 8rem;
|
||||
}
|
||||
|
||||
.action-col .button{
|
||||
margin: 0.1rem;
|
||||
}
|
||||
|
||||
.user-role{
|
||||
background: url('../images/menu-bg.png') repeat #871d33;
|
||||
color: white;
|
||||
padding: 0.5rem 0;
|
||||
border-bottom: 4px solid #8e8e8e;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.user-role > a {
|
||||
color: white;
|
||||
}
|
||||
43
kspneo/static/css/messages.css
Normal file
43
kspneo/static/css/messages.css
Normal file
@@ -0,0 +1,43 @@
|
||||
.info, .success, .warning, .error, .validation {
|
||||
border: 1px solid;
|
||||
margin: 10px 0px;
|
||||
padding:15px 10px;
|
||||
}
|
||||
|
||||
.info:hover,
|
||||
.success:hover,
|
||||
.warning:hover,
|
||||
.error:hover,
|
||||
.validation:hover {
|
||||
cursor: default;
|
||||
filter: brightness(1.2);
|
||||
}
|
||||
|
||||
.info {
|
||||
color: #00529B;
|
||||
background-color: #BDE5F8;
|
||||
/*
|
||||
background-image: url('../images/knobs/info.png');
|
||||
*/
|
||||
}
|
||||
.success {
|
||||
color: #4F8A10;
|
||||
background-color: #DFF2BF;
|
||||
/*
|
||||
background-image:url('../images/knobs/success.png');
|
||||
*/
|
||||
}
|
||||
.warning {
|
||||
color: #9F6000;
|
||||
background-color: #FEEFB3;
|
||||
/*
|
||||
background-image: url('../images/knobs/warning.png');
|
||||
*/
|
||||
}
|
||||
.error {
|
||||
color: #D8000C;
|
||||
background-color: #FFBABA;
|
||||
/*
|
||||
background-image: url('../images/knobs/error.png');
|
||||
*/
|
||||
}
|
||||
9877
kspneo/static/css/mulewf.css
Normal file
9877
kspneo/static/css/mulewf.css
Normal file
File diff suppressed because it is too large
Load Diff
BIN
kspneo/static/fonts/rlp-icons.woff
Normal file
BIN
kspneo/static/fonts/rlp-icons.woff
Normal file
Binary file not shown.
BIN
kspneo/static/images/csm_rlp-logo_8de1241483.png
Normal file
BIN
kspneo/static/images/csm_rlp-logo_8de1241483.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.3 KiB |
BIN
kspneo/static/images/header-bg.png
Normal file
BIN
kspneo/static/images/header-bg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
BIN
kspneo/static/images/menu-bg.png
Normal file
BIN
kspneo/static/images/menu-bg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 935 B |
BIN
kspneo/static/images/rlp-logos-MUEEF.png
Normal file
BIN
kspneo/static/images/rlp-logos-MUEEF.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
2
kspneo/static/js/jquery-3.5.1.min.js
vendored
Normal file
2
kspneo/static/js/jquery-3.5.1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
13
kspneo/static/js/jquery-ui.min.js
vendored
Normal file
13
kspneo/static/js/jquery-ui.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
6445
kspneo/static/js/mulewf.min.js
vendored
Normal file
6445
kspneo/static/js/mulewf.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
12
kspneo/sub_settings/context_settings.py
Normal file
12
kspneo/sub_settings/context_settings.py
Normal file
@@ -0,0 +1,12 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||
Created on: 16.11.20
|
||||
|
||||
"""
|
||||
|
||||
BASE_TITLE = "konova"
|
||||
BASE_FRONTEND_TITLE = "Kompensationsverzeichnis Service Portal"
|
||||
WIKI_URL = "https://dienste.naturschutz.rlp.de/doku/doku.php?id=ksp:start"
|
||||
RENDER_HEADER = False
|
||||
230
kspneo/sub_settings/django_settings.py
Normal file
230
kspneo/sub_settings/django_settings.py
Normal file
@@ -0,0 +1,230 @@
|
||||
"""
|
||||
Django settings for konova project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 3.1.3.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/3.1/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/3.1/ref/settings/
|
||||
"""
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = os.path.dirname(
|
||||
os.path.dirname(
|
||||
os.path.dirname(
|
||||
os.path.abspath(
|
||||
__file__
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = '5=9-)2)h$u9=!zrhia9=lj-2#cpcb8=#$7y+)l$5tto$3q(n_+'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
|
||||
# Authentication settings
|
||||
LOGIN_URL = "/login/"
|
||||
|
||||
# Session settings
|
||||
SESSION_COOKIE_AGE = 30 * 60 # 30 minutes
|
||||
SESSION_SAVE_EVERY_REQUEST = True
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'dal',
|
||||
'dal_select2',
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'django.contrib.gis',
|
||||
'simple_sso.sso_server',
|
||||
'django_tables2',
|
||||
'fontawesome_5',
|
||||
'konova',
|
||||
'compensation',
|
||||
'intervention',
|
||||
'process',
|
||||
'organisation',
|
||||
]
|
||||
if DEBUG:
|
||||
INSTALLED_APPS += [
|
||||
'debug_toolbar',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
"django.middleware.locale.LocaleMiddleware",
|
||||
]
|
||||
if DEBUG:
|
||||
MIDDLEWARE += [
|
||||
"debug_toolbar.middleware.DebugToolbarMiddleware",
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'konova.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [
|
||||
os.path.join(BASE_DIR, "templates"),
|
||||
],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
WSGI_APPLICATION = 'konova.wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.contrib.gis.db.backends.postgis',
|
||||
'NAME': 'konova',
|
||||
'USER': 'postgres',
|
||||
'HOST': '127.0.0.1',
|
||||
'PORT': '5432',
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/3.1/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
DEFAULT_DATE_TIME_FORMAT = 'YYYY-MM-DD hh:mm:ss'
|
||||
|
||||
TIME_ZONE = 'Europe/Berlin'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
LOCALE_PATHS = (
|
||||
os.path.join(BASE_DIR, 'locale'),
|
||||
)
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/3.1/howto/static-files/
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, "static")
|
||||
STATICFILES_DIRS = [
|
||||
os.path.join(BASE_DIR, 'konova/static'),
|
||||
]
|
||||
|
||||
# DJANGO DEBUG TOOLBAR
|
||||
INTERNAL_IPS = [
|
||||
"127.0.0.1"
|
||||
]
|
||||
DEBUG_TOOLBAR_CONFIG = {
|
||||
"DISABLE_PANELS": {
|
||||
'debug_toolbar.panels.versions.VersionsPanel',
|
||||
'debug_toolbar.panels.timer.TimerPanel',
|
||||
'debug_toolbar.panels.settings.SettingsPanel',
|
||||
'debug_toolbar.panels.headers.HeadersPanel',
|
||||
'debug_toolbar.panels.request.RequestPanel',
|
||||
'debug_toolbar.panels.sql.SQLPanel',
|
||||
'debug_toolbar.panels.staticfiles.StaticFilesPanel',
|
||||
'debug_toolbar.panels.templates.TemplatesPanel',
|
||||
'debug_toolbar.panels.cache.CachePanel',
|
||||
'debug_toolbar.panels.signals.SignalsPanel',
|
||||
'debug_toolbar.panels.logging.LoggingPanel',
|
||||
'debug_toolbar.panels.redirects.RedirectsPanel',
|
||||
'debug_toolbar.panels.profiling.ProfilingPanel',
|
||||
}
|
||||
}
|
||||
|
||||
# EMAIL (see https://docs.djangoproject.com/en/dev/topics/email/)
|
||||
DEFAULT_FROM_EMAIL = "bot@arneo.de" # The default email address for the 'from' element
|
||||
EMAIL_HOST = "localhost"
|
||||
EMAIL_PORT = "1025"
|
||||
#EMAIL_HOST_USER = ""
|
||||
#EMAIL_HOST_PASSWORD = ""
|
||||
EMAIL_USE_TLS = False
|
||||
EMAIL_USE_SSL = False
|
||||
|
||||
# LOGGING
|
||||
BASIC_LOGGER = "logger"
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
'formatters': {
|
||||
'verbose': {
|
||||
'format': '{levelname} {asctime} {module}: {message}',
|
||||
'style': '{',
|
||||
},
|
||||
'simple': {
|
||||
'format': '{levelname} {message}',
|
||||
'style': '{',
|
||||
},
|
||||
},
|
||||
'handlers': {
|
||||
'log_to_file': {
|
||||
'level': 'DEBUG',
|
||||
'class': 'logging.handlers.RotatingFileHandler',
|
||||
'filename': '{}/logs/error.log'.format(BASE_DIR),
|
||||
'maxBytes': 1024*1024*5, # 5 MB
|
||||
'backupCount': 5,
|
||||
'formatter': 'verbose',
|
||||
},
|
||||
},
|
||||
'loggers': {
|
||||
BASIC_LOGGER: {
|
||||
'handlers': ['log_to_file'],
|
||||
'level': 'INFO',
|
||||
'propagate': True,
|
||||
},
|
||||
},
|
||||
}
|
||||
5
kspneo/templates/kspneo/choiceColumnForm.html
Normal file
5
kspneo/templates/kspneo/choiceColumnForm.html
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
<form action="{{ form.action_url }}" method="post">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
</form>
|
||||
8
kspneo/templates/kspneo/form.html
Normal file
8
kspneo/templates/kspneo/form.html
Normal file
@@ -0,0 +1,8 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block body %}
|
||||
<div class="column">
|
||||
{% include 'generic_table_form.html' %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
44
kspneo/templates/kspneo/home.html
Normal file
44
kspneo/templates/kspneo/home.html
Normal file
@@ -0,0 +1,44 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block body_middle %}
|
||||
<h1>Kompensationsverzeichnis</h1>
|
||||
<h2>Service Portal</h2>
|
||||
<hr>
|
||||
{% if user.is_anonymous %}
|
||||
<a href="{% url 'simple-sso-login' %}">
|
||||
<button class="button middle">
|
||||
{% trans 'Proceed with login' %}
|
||||
</button>
|
||||
</a>
|
||||
{% else %}
|
||||
<article>
|
||||
{% trans 'Logged in as' %} <strong>{{ user.username }}</strong>
|
||||
<br>
|
||||
{% trans 'Last login on' %} {{ user.last_login }}
|
||||
</article>
|
||||
|
||||
<form action="{{form.action_url}}" method="post">
|
||||
{% csrf_token %}
|
||||
<table>
|
||||
{% comment %}
|
||||
This is an alternative to using the <article></article>
|
||||
<tr>
|
||||
<td>{% trans 'Logged in as' %}</td>
|
||||
<td><strong>{{ user.username }}</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Last login on' %}</td>
|
||||
<td><strong>{{ user.last_login }}</strong></td>
|
||||
</tr>
|
||||
{% endcomment %}
|
||||
{% for field in form %}
|
||||
<tr>
|
||||
<td>{{ field.label }}</td>
|
||||
<td>{{ field }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
7
kspneo/templatetags/__init__.py
Normal file
7
kspneo/templatetags/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||
Created on: 04.12.20
|
||||
|
||||
"""
|
||||
17
kspneo/templatetags/custom_tags.py
Normal file
17
kspneo/templatetags/custom_tags.py
Normal file
@@ -0,0 +1,17 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||
Created on: 04.12.20
|
||||
|
||||
"""
|
||||
from django import template
|
||||
|
||||
from process.settings import PROCESS_STATE_STRINGS
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.filter
|
||||
def resolve_process_state(value):
|
||||
return PROCESS_STATE_STRINGS.get(value, None)
|
||||
47
kspneo/urls.py
Normal file
47
kspneo/urls.py
Normal file
@@ -0,0 +1,47 @@
|
||||
"""konova URL Configuration
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/3.1/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: from django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
import debug_toolbar
|
||||
from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
from simple_sso.sso_client.client import Client
|
||||
|
||||
from konova.autocompletes import OrganisationAutocomplete, NonOfficialOrganisationAutocomplete
|
||||
from konova.settings import SSO_SERVER, SSO_PUBLIC_KEY, SSO_PRIVATE_KEY, DEBUG
|
||||
from konova.views import logout_view, home_view
|
||||
|
||||
sso_client = Client(SSO_SERVER, SSO_PUBLIC_KEY, SSO_PRIVATE_KEY)
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
path('login/', include(sso_client.get_urls())),
|
||||
path('logout/', logout_view, name="logout"),
|
||||
path('', home_view, name="home"),
|
||||
path('process/', include("process.urls")),
|
||||
path('intervention/', include("intervention.urls")),
|
||||
path('compensation/', include("compensation.urls")),
|
||||
path('eco-account/', include("process.urls")),
|
||||
path('ema/', include("process.urls")),
|
||||
path('organisation/', include("organisation.urls")),
|
||||
path('user/', include("process.urls")),
|
||||
|
||||
# Autocomplete paths
|
||||
path("atcmplt/orgs", OrganisationAutocomplete.as_view(), name="orgs-autocomplete"),
|
||||
path("atcmplt/orgs/other", NonOfficialOrganisationAutocomplete.as_view(), name="other-orgs-autocomplete"),
|
||||
]
|
||||
|
||||
if DEBUG:
|
||||
urlpatterns += [
|
||||
path('__debug__/', include(debug_toolbar.urls)),
|
||||
]
|
||||
22
kspneo/utils/generators.py
Normal file
22
kspneo/utils/generators.py
Normal file
@@ -0,0 +1,22 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||
Created on: 09.11.20
|
||||
|
||||
"""
|
||||
import random
|
||||
import string
|
||||
|
||||
|
||||
def generate_random_string(length: int, only_numbers: bool = False) -> str:
|
||||
"""
|
||||
Generates a random string of variable length
|
||||
"""
|
||||
if only_numbers:
|
||||
elements = string.digits
|
||||
else:
|
||||
elements = string.ascii_letters
|
||||
|
||||
ret_val = "".join(random.choice(elements) for i in range(length))
|
||||
return ret_val
|
||||
52
kspneo/utils/mailer.py
Normal file
52
kspneo/utils/mailer.py
Normal file
@@ -0,0 +1,52 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||
Created on: 09.11.20
|
||||
|
||||
"""
|
||||
import logging
|
||||
|
||||
from django.core.mail import send_mail
|
||||
|
||||
from konova.sub_settings.django_settings import DEFAULT_FROM_EMAIL
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Mailer:
|
||||
"""
|
||||
A wrapper for the django internal mailing functionality
|
||||
"""
|
||||
from_mail = None
|
||||
to_mail = []
|
||||
fail_silently = False
|
||||
|
||||
# Optional. Can be changed using the constructor to authenticate on the smtp server using other credentials
|
||||
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]
|
||||
|
||||
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):
|
||||
"""
|
||||
Sends a mail with subject and message
|
||||
"""
|
||||
return send_mail(
|
||||
subject=subject,
|
||||
message=msg,
|
||||
from_email=self.from_mail,
|
||||
recipient_list=self.to_mail,
|
||||
fail_silently=self.fail_silently,
|
||||
auth_user=self.auth_user,
|
||||
auth_password=self.auth_password
|
||||
)
|
||||
45
kspneo/utils/session.py
Normal file
45
kspneo/utils/session.py
Normal file
@@ -0,0 +1,45 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||
Created on: 09.12.20
|
||||
|
||||
"""
|
||||
from django.http import HttpRequest
|
||||
from idna import unicode
|
||||
|
||||
from konova.models import RoleGroup
|
||||
from organisation.settings import ROLE_TYPE_STRINGS
|
||||
|
||||
CURRENT_ROLE_ID = "current_role"
|
||||
|
||||
|
||||
def set_session_user_role(request: HttpRequest, role_group: RoleGroup) -> dict:
|
||||
""" Set the user session to an active role
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The user request
|
||||
role_group (RoleGroup): The selected role group
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
current_role = {}
|
||||
if role_group is not None:
|
||||
current_role["type"] = unicode(ROLE_TYPE_STRINGS.get(role_group.role.type))
|
||||
current_role["org"] = role_group.organisation.__str__()
|
||||
current_role["id"] = role_group.id
|
||||
request.session[CURRENT_ROLE_ID] = current_role
|
||||
return current_role
|
||||
|
||||
|
||||
def get_session_user_role(request: HttpRequest) -> dict:
|
||||
""" Returns the current role chosen by a user for this session
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The used request
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return request.session.get(CURRENT_ROLE_ID, {})
|
||||
121
kspneo/utils/tables.py
Normal file
121
kspneo/utils/tables.py
Normal file
@@ -0,0 +1,121 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||
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.settings import PAGE_SIZE_DEFAULT, PAGE_PARAM, RESULTS_PER_PAGE_PARAM, PAGE_SIZE_OPTIONS
|
||||
|
||||
|
||||
class BaseTable(tables.tables.Table):
|
||||
results_per_page_choices = PAGE_SIZE_OPTIONS
|
||||
results_per_page_chosen = None
|
||||
results_per_page_parameter = RESULTS_PER_PAGE_PARAM
|
||||
add_new_entries = True
|
||||
add_new_url = None
|
||||
title = None
|
||||
|
||||
def __init__(self, request: HttpRequest = None, filter_set=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)
|
||||
self.results_per_page_chosen = int(request.GET.get(RESULTS_PER_PAGE_PARAM, PAGE_SIZE_DEFAULT))
|
||||
try:
|
||||
self.paginate(
|
||||
page=request.GET.get(PAGE_PARAM, 1),
|
||||
per_page=self.results_per_page_chosen,
|
||||
)
|
||||
except (PageNotAnInteger, EmptyPage) as e:
|
||||
self.paginate(
|
||||
page=1,
|
||||
per_page=self.results_per_page_chosen,
|
||||
)
|
||||
|
||||
def render_link(self, tooltip: str, href: str, txt: str, new_tab: bool = False):
|
||||
"""
|
||||
Returns an <a> html element using given parameters
|
||||
"""
|
||||
new_tab = "_blank" if new_tab else "_self"
|
||||
return format_html(
|
||||
"<a href={} target='{}' title='{}'>{}</a>",
|
||||
href,
|
||||
new_tab,
|
||||
tooltip,
|
||||
txt,
|
||||
)
|
||||
|
||||
def render_delete_btn(self, tooltip: str = None, href: str = None):
|
||||
"""
|
||||
Returns a remover icon with <a> support as html element using given parameters
|
||||
"""
|
||||
return format_html(
|
||||
"<a href={} title='{}'><button class='button small'><em class='fas fa-trash-alt'></em></button></a>",
|
||||
href,
|
||||
tooltip,
|
||||
)
|
||||
|
||||
def render_edit_btn(self, tooltip: str = None, href: str = None):
|
||||
"""
|
||||
Returns a remover icon with <a> support as html element using given parameters
|
||||
"""
|
||||
return format_html(
|
||||
"<a href={} title='{}'><button class='button small'><em class='fas fa-edit'></em></button></a>",
|
||||
href,
|
||||
tooltip,
|
||||
)
|
||||
|
||||
def render_open_btn(self, tooltip: str = None, href: str = None, new_tab: bool = False):
|
||||
"""
|
||||
Returns a remover icon with <a> support as html element using given parameters
|
||||
"""
|
||||
return format_html(
|
||||
"<a href={} title='{}' target='{}'><button class='button small'><em class='fas fa-sign-in-alt'></em></button></a>",
|
||||
href,
|
||||
tooltip,
|
||||
"_blank" if new_tab else ""
|
||||
)
|
||||
|
||||
def render_boolean(self, tooltip: str = None, val: bool = False):
|
||||
"""
|
||||
Returns a remover icon with <a> support as html element using given parameters
|
||||
"""
|
||||
icon = "fas fa-check-circle true" if val else "fas fa-times-circle false"
|
||||
return format_html(
|
||||
"<em title='{}' class='{}'></em>",
|
||||
tooltip,
|
||||
icon
|
||||
)
|
||||
|
||||
|
||||
class ChoicesColumnForm(BaseForm):
|
||||
select = forms.ChoiceField(
|
||||
choices=[],
|
||||
label="",
|
||||
label_suffix="",
|
||||
widget=forms.Select(
|
||||
attrs={
|
||||
"onchange": "submit();",
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
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
|
||||
67
kspneo/views.py
Normal file
67
kspneo/views.py
Normal file
@@ -0,0 +1,67 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||
Created on: 16.11.20
|
||||
|
||||
"""
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth import logout
|
||||
from django.http import HttpRequest
|
||||
from django.shortcuts import redirect, render
|
||||
|
||||
from konova.contexts import BaseContext
|
||||
from konova.forms import ChangeUserRoleForm
|
||||
from konova.settings import SSO_SERVER_BASE
|
||||
from konova.utils.session import get_session_user_role
|
||||
|
||||
|
||||
def logout_view(request: HttpRequest):
|
||||
"""
|
||||
Logout route for ending the session manually.
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The used request object
|
||||
|
||||
Returns:
|
||||
A redirect
|
||||
"""
|
||||
logout(request)
|
||||
return redirect(SSO_SERVER_BASE)
|
||||
|
||||
|
||||
def home_view(request: HttpRequest):
|
||||
"""
|
||||
Renders the landing page
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The used request object
|
||||
|
||||
Returns:
|
||||
A redirect
|
||||
"""
|
||||
template = "konova/home.html"
|
||||
|
||||
if request.method == "POST":
|
||||
form = ChangeUserRoleForm(
|
||||
request.POST or None,
|
||||
user=request.user,
|
||||
)
|
||||
if form.is_valid():
|
||||
role = form.save(request)
|
||||
messages.success(request, _("Role changed"))
|
||||
else:
|
||||
messages.error(request, _("Invalid role"))
|
||||
return redirect("home")
|
||||
else:
|
||||
# GET
|
||||
form = ChangeUserRoleForm(
|
||||
user=request.user,
|
||||
initial={"role": int(get_session_user_role(request).get("id", -1))},
|
||||
)
|
||||
additional_context = {
|
||||
"form": form,
|
||||
}
|
||||
context = BaseContext(request, additional_context).context
|
||||
return render(request, template, context)
|
||||
16
kspneo/wsgi.py
Normal file
16
kspneo/wsgi.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
WSGI config for konova project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/3.1/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'konova.settings')
|
||||
|
||||
application = get_wsgi_application()
|
||||
Reference in New Issue
Block a user