Refactoring to konova
@ -5,14 +5,13 @@ Contact: michel.peltriaux@sgdnord.rlp.de
 | 
			
		||||
Created on: 17.11.20
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
from django.contrib.auth.models import User
 | 
			
		||||
from django.contrib.gis.db import models
 | 
			
		||||
from django.utils.timezone import now
 | 
			
		||||
 | 
			
		||||
from compensation.settings import COMPENSATION_IDENTIFIER_LENGTH, COMPENSATION_IDENTIFIER_TEMPLATE
 | 
			
		||||
from konova.models import BaseObject, BaseResource
 | 
			
		||||
from konova.models import BaseObject, BaseResource, Geometry
 | 
			
		||||
from konova.utils.generators import generate_random_string
 | 
			
		||||
from process.models import Process
 | 
			
		||||
from organisation.models import Organisation
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CompensationControl(BaseResource):
 | 
			
		||||
@ -22,7 +21,7 @@ class CompensationControl(BaseResource):
 | 
			
		||||
    deadline = models.ForeignKey("konova.Deadline", on_delete=models.SET_NULL, null=True, blank=True)
 | 
			
		||||
    type = models.CharField(max_length=500, null=True, blank=True)
 | 
			
		||||
    expected_result = models.CharField(max_length=500, null=True, blank=True, help_text="The expected outcome, that needs to be controlled")
 | 
			
		||||
    by_authority = models.CharField(max_length=500, null=True, blank=True)
 | 
			
		||||
    by_authority = models.ForeignKey(Organisation, null=True, blank=True, on_delete=models.SET_NULL)
 | 
			
		||||
    comment = models.TextField()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -31,8 +30,7 @@ class CompensationState(models.Model):
 | 
			
		||||
    Compensations must define the state of an area before and after the compensation.
 | 
			
		||||
    """
 | 
			
		||||
    biotope_type = models.CharField(max_length=500, null=True, blank=True)
 | 
			
		||||
    amount = models.FloatField()
 | 
			
		||||
    unit = models.CharField(max_length=100, null=True, blank=True)
 | 
			
		||||
    surface = models.FloatField()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CompensationAction(BaseResource):
 | 
			
		||||
@ -50,22 +48,21 @@ class Compensation(BaseObject):
 | 
			
		||||
    The compensation holds information about which actions have to be performed until which date, who is in charge
 | 
			
		||||
    of this, which legal authority is the point of contact, and so on.
 | 
			
		||||
    """
 | 
			
		||||
    is_old_law = models.BooleanField(default=False)
 | 
			
		||||
    type = models.CharField(max_length=500, null=True, blank=True)
 | 
			
		||||
    registration_office = models.CharField(max_length=500, null=True, blank=True)  # ToDo: Really needed?
 | 
			
		||||
    process = models.ForeignKey("process.Process", related_name="compensations", on_delete=models.CASCADE)
 | 
			
		||||
    registration_office = models.ForeignKey(Organisation, on_delete=models.SET_NULL, null=True)
 | 
			
		||||
    conservation_office = models.ForeignKey(Organisation, on_delete=models.SET_NULL, null=True)
 | 
			
		||||
 | 
			
		||||
    ground_definitions = models.CharField(max_length=500, null=True, blank=True)  # ToDo: Need to be M2M to laws!
 | 
			
		||||
    action_definitions = models.CharField(max_length=500, null=True, blank=True)  # ToDo: Need to be M2M to laws!
 | 
			
		||||
 | 
			
		||||
    before_states = models.ManyToManyField(CompensationState, blank=True, related_name='+')
 | 
			
		||||
    after_states = models.ManyToManyField(CompensationState, blank=True, related_name='+')
 | 
			
		||||
    actions = models.ManyToManyField(CompensationAction)
 | 
			
		||||
 | 
			
		||||
    deadline_creation = models.ForeignKey("konova.Deadline", on_delete=models.SET_NULL, null=True, blank=True, related_name="deadline_creation")
 | 
			
		||||
    deadline_maintaining = models.ForeignKey("konova.Deadline", on_delete=models.SET_NULL, null=True, blank=True, related_name="deadline_maintaining")
 | 
			
		||||
    initial_states = models.ManyToManyField(CompensationState, blank=True, related_name='+')
 | 
			
		||||
    final_states = models.ManyToManyField(CompensationState, blank=True, related_name='+')
 | 
			
		||||
    geometry = models.MultiPolygonField(null=True, blank=True)
 | 
			
		||||
    documents = models.ManyToManyField("konova.Document", blank=True)
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return "{} of {}".format(self.type, self.process)
 | 
			
		||||
    geometry = models.ForeignKey(Geometry, null=True, blank=True, on_delete=models.SET_NULL)
 | 
			
		||||
    documents = models.ManyToManyField("konova.Document", blank=True)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def __generate_new_identifier() -> str:
 | 
			
		||||
@ -92,36 +89,8 @@ class Compensation(BaseObject):
 | 
			
		||||
            self.identifier = new_id
 | 
			
		||||
        super().save(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def get_role_objects(user: User, order_by: str = "-created_on"):
 | 
			
		||||
        """ Returns objects depending on the currently selected role of the user
 | 
			
		||||
 | 
			
		||||
            * REGISTRATIONOFFICE
 | 
			
		||||
                * User can see the processes where registration_office is set to the organisation of the currently selected role
 | 
			
		||||
                * User can see self-created processes
 | 
			
		||||
            * LICENSINGOFFICE
 | 
			
		||||
                * same
 | 
			
		||||
            * DATAPROVIDER
 | 
			
		||||
                * User can see only self-created processes
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            user (User): The performing user
 | 
			
		||||
            order_by (str): Order by which Process attribute
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        role = user.current_role
 | 
			
		||||
        if role is None:
 | 
			
		||||
            return Compensation.objects.none()
 | 
			
		||||
        processes = Process.get_role_objects(user, order_by)
 | 
			
		||||
        processes.prefetch_related("compensations")
 | 
			
		||||
        compensations = []
 | 
			
		||||
        [compensations.extend(process.compensations.all()) for process in processes]
 | 
			
		||||
        return compensations
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EcoAccount(BaseResource):
 | 
			
		||||
class EcoAccount(Compensation):
 | 
			
		||||
    """
 | 
			
		||||
    An eco account is a kind of 'prepaid' compensation. It can be compared to an account that already has been filled
 | 
			
		||||
    with some kind of currency. From this account one is able to 'withdraw' currency for current projects.
 | 
			
		||||
@ -129,17 +98,4 @@ class EcoAccount(BaseResource):
 | 
			
		||||
    'Withdrawing' can only be applied by shrinking the size of the available geometry and declaring the withdrawed
 | 
			
		||||
    geometry as a compensation for a process.
 | 
			
		||||
    """
 | 
			
		||||
    is_old_law = models.BooleanField(default=False)
 | 
			
		||||
    type = models.CharField(max_length=500, null=True, blank=True)
 | 
			
		||||
    licensing_authority_document_identifier = models.CharField(max_length=500, null=True, blank=True)
 | 
			
		||||
    registration_office = models.CharField(max_length=500, null=True, blank=True)
 | 
			
		||||
    handler = models.CharField(max_length=500, null=True, blank=True)
 | 
			
		||||
    handler_comments = models.TextField()
 | 
			
		||||
    geometry = models.GeometryCollectionField()
 | 
			
		||||
    documents = models.ManyToManyField("konova.Document")
 | 
			
		||||
    initial_states = models.ManyToManyField(CompensationState, blank=True, related_name='+')
 | 
			
		||||
    final_states = models.ManyToManyField(CompensationState, blank=True, related_name='+')
 | 
			
		||||
    actions = models.ManyToManyField(CompensationAction)
 | 
			
		||||
    deadline_maintaining = models.ForeignKey("konova.Deadline", on_delete=models.SET_NULL, null=True, blank=True)
 | 
			
		||||
    deadline_other = models.ManyToManyField("konova.Deadline", blank=True, related_name='+')
 | 
			
		||||
    comments = models.TextField()
 | 
			
		||||
    handler = models.CharField(max_length=500, null=True, blank=True, help_text="Who is responsible for handling the actions")
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,6 @@ from konova.decorators import *
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@login_required
 | 
			
		||||
@resolve_user_role
 | 
			
		||||
def index_view(request: HttpRequest):
 | 
			
		||||
    """
 | 
			
		||||
    Renders the index view for compensation
 | 
			
		||||
@ -22,7 +21,7 @@ def index_view(request: HttpRequest):
 | 
			
		||||
    """
 | 
			
		||||
    template = "generic_index.html"
 | 
			
		||||
    user = request.user
 | 
			
		||||
    compensations = Compensation.get_role_objects(user)
 | 
			
		||||
    compensations = None # ToDo
 | 
			
		||||
    table = CompensationTable(
 | 
			
		||||
        request=request,
 | 
			
		||||
        queryset=compensations
 | 
			
		||||
 | 
			
		||||
@ -7,11 +7,11 @@ class InterventionAdmin(admin.ModelAdmin):
 | 
			
		||||
    list_display = [
 | 
			
		||||
        "id",
 | 
			
		||||
        "title",
 | 
			
		||||
        "type",
 | 
			
		||||
        "process_type",
 | 
			
		||||
        "handler",
 | 
			
		||||
        "created_on",
 | 
			
		||||
        "is_active",
 | 
			
		||||
        "is_deleted",
 | 
			
		||||
        "created_on",
 | 
			
		||||
        "deleted_on",
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -10,10 +10,9 @@ from django.contrib.gis.db import models
 | 
			
		||||
from django.utils.timezone import now
 | 
			
		||||
 | 
			
		||||
from intervention.settings import INTERVENTION_IDENTIFIER_LENGTH, INTERVENTION_IDENTIFIER_TEMPLATE
 | 
			
		||||
from konova.models import BaseObject
 | 
			
		||||
from konova.models import BaseObject, Geometry
 | 
			
		||||
from konova.utils.generators import generate_random_string
 | 
			
		||||
from organisation.enums import RoleTypeEnum
 | 
			
		||||
from process.models import Process
 | 
			
		||||
from organisation.models import Organisation
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Intervention(BaseObject):
 | 
			
		||||
@ -22,21 +21,30 @@ class Intervention(BaseObject):
 | 
			
		||||
 | 
			
		||||
    A process consists of exactly one intervention and one or more compensation
 | 
			
		||||
    """
 | 
			
		||||
    type = models.CharField(max_length=500, null=True, blank=True)
 | 
			
		||||
    registration_office = models.ForeignKey(Organisation, on_delete=models.SET_NULL, null=True)
 | 
			
		||||
    registration_file_number = models.CharField(max_length=1000, blank=True, null=True)
 | 
			
		||||
    registration_date = models.DateTimeField(null=True, blank=True)
 | 
			
		||||
 | 
			
		||||
    conservation_office = models.ForeignKey(Organisation, on_delete=models.SET_NULL, null=True)
 | 
			
		||||
    conservations_file_number = models.CharField(max_length=1000, blank=True, null=True)
 | 
			
		||||
 | 
			
		||||
    process_type = models.CharField(max_length=500, null=True, blank=True)
 | 
			
		||||
    law = models.CharField(max_length=500, null=True, blank=True)
 | 
			
		||||
    handler = models.CharField(max_length=500, null=True, blank=True)
 | 
			
		||||
    data_provider = models.ForeignKey("organisation.Organisation", on_delete=models.SET_NULL, null=True, blank=True)
 | 
			
		||||
    data_provider_detail = models.CharField(max_length=500, null=True, blank=True)
 | 
			
		||||
    geometry = models.MultiPolygonField(null=True, blank=True)
 | 
			
		||||
    process = models.OneToOneField("process.Process", on_delete=models.CASCADE, null=True, blank=True, related_name="intervention")
 | 
			
		||||
    geometry = models.ForeignKey(Geometry, null=True, blank=True, on_delete=models.SET_NULL)
 | 
			
		||||
    documents = models.ManyToManyField("konova.Document", blank=True)
 | 
			
		||||
 | 
			
		||||
    # Refers to "verzeichnen"
 | 
			
		||||
    recorded_on = models.DateTimeField(default=None)
 | 
			
		||||
    recorded_by = models.ForeignKey(User, null=True, blank=True, on_delete=models.SET_NULL)
 | 
			
		||||
 | 
			
		||||
    # Holds which intervention is simply a newer version of this dataset
 | 
			
		||||
    next_version = models.ForeignKey("Intervention", null=True, on_delete=models.DO_NOTHING)
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return "{} by {}".format(self.type, self.handler)
 | 
			
		||||
 | 
			
		||||
    def delete(self, *args, **kwargs):
 | 
			
		||||
        if self.process is not None:
 | 
			
		||||
            self.process.delete()
 | 
			
		||||
        super().delete(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
@ -63,29 +71,3 @@ class Intervention(BaseObject):
 | 
			
		||||
                new_id = self.__generate_new_identifier()
 | 
			
		||||
            self.identifier = new_id
 | 
			
		||||
        super().save(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def get_role_objects(user: User, order_by: str = "-created_on") -> list:
 | 
			
		||||
        """ Returns objects depending on the currently selected role of the user
 | 
			
		||||
 | 
			
		||||
            * REGISTRATIONOFFICE
 | 
			
		||||
                * User can see the processes where registration_office is set to the organisation of the currently selected role
 | 
			
		||||
                * User can see self-created processes
 | 
			
		||||
            * LICENSINGOFFICE
 | 
			
		||||
                * same
 | 
			
		||||
            * DATAPROVIDER
 | 
			
		||||
                * User can see only self-created processes
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            user (User): The performing user
 | 
			
		||||
            order_by (str): Order by which Process attribute
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        role = user.current_role
 | 
			
		||||
        if role is None:
 | 
			
		||||
            return Intervention.objects.none()
 | 
			
		||||
        processes = Process.get_role_objects(user, order_by)
 | 
			
		||||
        interventions = [process.intervention for process in processes]
 | 
			
		||||
        return interventions
 | 
			
		||||
@ -15,7 +15,7 @@ from process.models import Process
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@login_required
 | 
			
		||||
@resolve_user_role
 | 
			
		||||
 | 
			
		||||
def index_view(request: HttpRequest):
 | 
			
		||||
    """
 | 
			
		||||
    Renders the index view for process
 | 
			
		||||
@ -28,7 +28,7 @@ def index_view(request: HttpRequest):
 | 
			
		||||
    """
 | 
			
		||||
    template = "generic_index.html"
 | 
			
		||||
    user = request.user
 | 
			
		||||
    interventions = Intervention.get_role_objects(user)
 | 
			
		||||
    interventions = Intervention # ToDo
 | 
			
		||||
    table = InterventionTable(
 | 
			
		||||
        request=request,
 | 
			
		||||
        queryset=interventions
 | 
			
		||||
 | 
			
		||||
@ -7,9 +7,7 @@ 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
 | 
			
		||||
from konova.sub_settings.context_settings import BASE_TITLE, WIKI_URL, BASE_FRONTEND_TITLE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BaseContext:
 | 
			
		||||
@ -22,7 +20,6 @@ class BaseContext:
 | 
			
		||||
        "language": "en",
 | 
			
		||||
        "wiki_url": WIKI_URL,
 | 
			
		||||
        "user": None,
 | 
			
		||||
        "render_header": RENDER_HEADER,
 | 
			
		||||
        "current_role": None,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -33,21 +30,3 @@ class BaseContext:
 | 
			
		||||
 | 
			
		||||
        # 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
 | 
			
		||||
							
								
								
									
										44
									
								
								konova/decorators.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,44 @@
 | 
			
		||||
"""
 | 
			
		||||
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.shortcuts import redirect
 | 
			
		||||
from django.urls import reverse
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
@ -13,7 +13,6 @@ 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"
 | 
			
		||||
@ -27,7 +26,6 @@ class Command(BaseCommand):
 | 
			
		||||
            with transaction.atomic():
 | 
			
		||||
                self.__init_superuser()
 | 
			
		||||
                self.__init_test_organisation()
 | 
			
		||||
                self.__init_role_types()
 | 
			
		||||
                self.__init_role_groups()
 | 
			
		||||
        except KeyboardInterrupt:
 | 
			
		||||
            self.__break_line()
 | 
			
		||||
@ -76,28 +74,6 @@ class Command(BaseCommand):
 | 
			
		||||
        )
 | 
			
		||||
        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
 | 
			
		||||
 | 
			
		||||
@ -5,32 +5,27 @@ 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,
 | 
			
		||||
        },
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
@ -38,21 +33,17 @@ 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,
 | 
			
		||||
        },
 | 
			
		||||
    ]
 | 
			
		||||
@ -7,11 +7,9 @@ Created on: 17.11.20
 | 
			
		||||
"""
 | 
			
		||||
import uuid
 | 
			
		||||
 | 
			
		||||
from django.contrib.auth.models import User, Group
 | 
			
		||||
from django.contrib.auth.models import User
 | 
			
		||||
from django.contrib.gis.db.models import MultiPolygonField
 | 
			
		||||
from django.db import models
 | 
			
		||||
from django.db.models import QuerySet
 | 
			
		||||
 | 
			
		||||
from organisation.enums import RoleTypeEnum
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BaseResource(models.Model):
 | 
			
		||||
@ -22,8 +20,6 @@ class BaseResource(models.Model):
 | 
			
		||||
        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)
 | 
			
		||||
 | 
			
		||||
@ -39,6 +35,9 @@ class BaseObject(BaseResource):
 | 
			
		||||
    """
 | 
			
		||||
    identifier = models.CharField(max_length=1000, null=True, blank=True)
 | 
			
		||||
    title = models.CharField(max_length=1000, null=True, blank=True)
 | 
			
		||||
    deleted_on = models.DateTimeField(null=True)
 | 
			
		||||
    deleted_by = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
 | 
			
		||||
    comment = models.TextField()
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        abstract = True
 | 
			
		||||
@ -64,41 +63,8 @@ class Document(BaseResource):
 | 
			
		||||
    comment = models.TextField()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RoleType(BaseResource):
 | 
			
		||||
class Geometry(BaseResource):
 | 
			
		||||
    """
 | 
			
		||||
    Defines different role types
 | 
			
		||||
    Outsourced geometry model so multiple versions of the same object can refer to the same geometry if it is not changed
 | 
			
		||||
    """
 | 
			
		||||
    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
 | 
			
		||||
        )
 | 
			
		||||
    geom = MultiPolygonField(null=True, blank=True)
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 6.3 KiB  | 
| 
		 Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB  | 
| 
		 Before Width: | Height: | Size: 935 B After Width: | Height: | Size: 935 B  | 
| 
		 Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB  | 
@ -9,4 +9,3 @@ 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
 | 
			
		||||
@ -56,6 +56,7 @@ INSTALLED_APPS = [
 | 
			
		||||
    'simple_sso.sso_server',
 | 
			
		||||
    'django_tables2',
 | 
			
		||||
    'fontawesome_5',
 | 
			
		||||
    'bootstrap4',
 | 
			
		||||
    'konova',
 | 
			
		||||
    'compensation',
 | 
			
		||||
    'intervention',
 | 
			
		||||
@ -28,7 +28,6 @@ urlpatterns = [
 | 
			
		||||
    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")),
 | 
			
		||||
@ -43,25 +43,6 @@ def home_view(request: HttpRequest):
 | 
			
		||||
    """
 | 
			
		||||
    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,
 | 
			
		||||
    }
 | 
			
		||||
    additional_context = {}
 | 
			
		||||
    context = BaseContext(request, additional_context).context
 | 
			
		||||
    return render(request, template, context)
 | 
			
		||||
@ -1,94 +0,0 @@
 | 
			
		||||
"""
 | 
			
		||||
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
 | 
			
		||||
@ -1,57 +0,0 @@
 | 
			
		||||
.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;
 | 
			
		||||
}
 | 
			
		||||
@ -1,43 +0,0 @@
 | 
			
		||||
.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');
 | 
			
		||||
*/
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										2
									
								
								kspneo/static/js/jquery-3.5.1.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
							
								
								
									
										13
									
								
								kspneo/static/js/jquery-ui.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
							
								
								
									
										6445
									
								
								kspneo/static/js/mulewf.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						@ -6,21 +6,3 @@ Created on: 07.12.20
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
from konova.enums import BaseEnum
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RoleTypeEnum(BaseEnum):
 | 
			
		||||
    """
 | 
			
		||||
    Defines the possible role types for organisation and users
 | 
			
		||||
    """
 | 
			
		||||
    DATAPROVIDER = "DATAPROVIDER"
 | 
			
		||||
    LICENSINGAUTHORITY = "LICENSINGAUTHORITY"
 | 
			
		||||
    REGISTRATIONOFFICE = "REGISTRATIONOFFICE"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class OrganisationTypeEnum(BaseEnum):
 | 
			
		||||
    """
 | 
			
		||||
    Defines the possible role types for organisation and users
 | 
			
		||||
    """
 | 
			
		||||
    OFFICIAL = "OFFICIAL"
 | 
			
		||||
    COMPANY = "COMPANY"
 | 
			
		||||
    NGO = "NGO"
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,6 @@
 | 
			
		||||
from django.db import models
 | 
			
		||||
 | 
			
		||||
from konova.models import BaseResource
 | 
			
		||||
from organisation.enums import OrganisationTypeEnum
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Organisation(BaseResource):
 | 
			
		||||
@ -12,7 +11,6 @@ class Organisation(BaseResource):
 | 
			
		||||
    phone = models.CharField(max_length=500, null=True, blank=True)
 | 
			
		||||
    email = models.EmailField(max_length=500, null=True, blank=True)
 | 
			
		||||
    facsimile = models.CharField(max_length=500, null=True, blank=True)
 | 
			
		||||
    type = models.CharField(max_length=255, choices=OrganisationTypeEnum.as_choices(drop_empty_choice=True), null=True, blank=True)
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return self.name
 | 
			
		||||
@ -6,17 +6,3 @@ Created on: 07.12.20
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
from organisation.enums import OrganisationTypeEnum as ote
 | 
			
		||||
from organisation.enums import RoleTypeEnum as rte
 | 
			
		||||
 | 
			
		||||
ORGANISATION_ROLE_STRINGS = {
 | 
			
		||||
    ote.OFFICIAL.value: _("Official"),
 | 
			
		||||
    ote.COMPANY.value: _("Company"),
 | 
			
		||||
    ote.NGO.value: _("NGO"),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ROLE_TYPE_STRINGS = {
 | 
			
		||||
    rte.DATAPROVIDER.value: _("Data provider"),
 | 
			
		||||
    rte.LICENSINGAUTHORITY.value: _("Licencing Authority"),
 | 
			
		||||
    rte.REGISTRATIONOFFICE.value: _("Registration office"),
 | 
			
		||||
}
 | 
			
		||||
@ -1,38 +0,0 @@
 | 
			
		||||
from django.contrib import admin
 | 
			
		||||
 | 
			
		||||
from process.models import Process
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def activate_process(modeladmin, request, queryset):
 | 
			
		||||
    for process in queryset:
 | 
			
		||||
        process.activate()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def deactivate_process(modeladmin, request, queryset):
 | 
			
		||||
    for process in queryset:
 | 
			
		||||
        process.deactivate()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
activate_process.short_description = "Activate selected process"
 | 
			
		||||
deactivate_process.short_description = "Deactivate selected process"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ProcessAdmin(admin.ModelAdmin):
 | 
			
		||||
    list_display = [
 | 
			
		||||
        "id",
 | 
			
		||||
        "licensing_authority",
 | 
			
		||||
        "licensing_authority_document_identifier",
 | 
			
		||||
        "registration_office",
 | 
			
		||||
        "registration_office_document_identifier",
 | 
			
		||||
        "is_active",
 | 
			
		||||
        "is_deleted",
 | 
			
		||||
    ]
 | 
			
		||||
    actions = [
 | 
			
		||||
        activate_process,
 | 
			
		||||
        deactivate_process,
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
admin.site.register(Process, ProcessAdmin)
 | 
			
		||||
@ -1,5 +0,0 @@
 | 
			
		||||
from django.apps import AppConfig
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ProcessConfig(AppConfig):
 | 
			
		||||
    name = 'process'
 | 
			
		||||
							
								
								
									
										202
									
								
								process/enums.py
									
									
									
									
									
								
							
							
						
						@ -1,202 +0,0 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: michel.peltriaux@sgdnord.rlp.de
 | 
			
		||||
Created on: 25.11.20
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
from django.contrib.auth.models import User
 | 
			
		||||
 | 
			
		||||
from konova.enums import BaseEnum
 | 
			
		||||
from organisation.enums import RoleTypeEnum
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ProcessStateEnum(BaseEnum):
 | 
			
		||||
    """
 | 
			
		||||
    ProcessStates define in which state a process can be:
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
            Only the user can see and edit this process. Not viewable by any others
 | 
			
		||||
        accessible
 | 
			
		||||
            The user and all participated users can see this process. Only the responsible next user can edit
 | 
			
		||||
            the process
 | 
			
		||||
        licensed
 | 
			
		||||
            The user and all participated users can see this process. Only the responsible next user can edit
 | 
			
		||||
            the process
 | 
			
		||||
        official
 | 
			
		||||
            The user and all participated users can see this process. Only the registration office user can now
 | 
			
		||||
            change details. If a process is changed in this state, the state will be set back to accessible, so all
 | 
			
		||||
            participated users need to take a look on the changed data again.
 | 
			
		||||
        recorded [Will be set automatically after certain time]
 | 
			
		||||
            The user and all participated users can see this process. No one can edit data. To change any details,
 | 
			
		||||
            the process has to be set to accessible manually again.
 | 
			
		||||
    """
 | 
			
		||||
    PRIVATE = 0
 | 
			
		||||
    ACCESSIBLE = 1
 | 
			
		||||
    LICENSED = 2
 | 
			
		||||
    OFFICIAL = 3
 | 
			
		||||
    RECORDED = 4
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def as_choices(cls, drop_empty_choice: bool = False) -> list:
 | 
			
		||||
        """ Extends as_choices, so choices will be translated
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            drop_empty_choice (bool): Whether the empty choice shall be dropped or not
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            trans_choices (list): Translated choices
 | 
			
		||||
        """
 | 
			
		||||
        choices = super().as_choices(drop_empty_choice)
 | 
			
		||||
        return ProcessStateEnum.__translate_choices(choices)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def __translate_choices(choices: list) -> list:
 | 
			
		||||
        """ Translates a list of prepared but untranslated choices
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            choices (list): A list of tuple chocies
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            choices (list): The same list but translated
 | 
			
		||||
        """
 | 
			
		||||
        from process.settings import PROCESS_STATE_STRINGS
 | 
			
		||||
        trans_choices = []
 | 
			
		||||
        # Translate
 | 
			
		||||
        for choice in choices:
 | 
			
		||||
            if choice[0] is not None:
 | 
			
		||||
                choice = list(choice)
 | 
			
		||||
                trans = PROCESS_STATE_STRINGS.get(choice[0])
 | 
			
		||||
                choice[1] = trans
 | 
			
		||||
                choice = tuple(choice)
 | 
			
		||||
            trans_choices.append(choice)
 | 
			
		||||
        return trans_choices
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def is_state(cls, state: int) -> bool:
 | 
			
		||||
        """ Checks whether the given state is a valid Enum
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            state (int): The state to be checked
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            is_valid (bool)
 | 
			
		||||
        """
 | 
			
		||||
        valid_vals = {enum.value for enum in cls}
 | 
			
		||||
        return state in valid_vals
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def as_role_choices(cls, user: User) -> list:
 | 
			
		||||
        """ Checks whether the given state is a valid Enum
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            user (User): The performing user
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            is_valid (bool)
 | 
			
		||||
        """
 | 
			
		||||
        role = user.current_role
 | 
			
		||||
        if role is None:
 | 
			
		||||
            return []
 | 
			
		||||
        role_type = role.role.type
 | 
			
		||||
        role_type_enum = RoleTypeEnum[role_type]
 | 
			
		||||
        choices = PROCESS_ROLE_STATES.get(role_type_enum)
 | 
			
		||||
        choices = [(enum.value, enum.name) for enum in choices]
 | 
			
		||||
        choices = ProcessStateEnum.__translate_choices(choices)
 | 
			
		||||
        return choices
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def as_next_role_choices(cls, user: User, current_state: int, is_owner: bool = False) -> list:
 | 
			
		||||
        """ Returns a list of valid choices depending on the current role of the user and the current state
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            user (User): The performing user
 | 
			
		||||
            current_state (int): The current state of the process
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            choices (list): A list of valid choices
 | 
			
		||||
        """
 | 
			
		||||
        role = user.current_role
 | 
			
		||||
        if role is None:
 | 
			
		||||
            return []
 | 
			
		||||
        role_type = role.role.type
 | 
			
		||||
        role_type_enum = RoleTypeEnum[role_type]
 | 
			
		||||
 | 
			
		||||
        # Merge the possible choices depending on the current user role
 | 
			
		||||
        # with the possible choices depending on the process state
 | 
			
		||||
        role_choices = PROCESS_ROLE_STATES.get(role_type_enum)
 | 
			
		||||
        status_choices = PROCESS_STATE_NEXT_CHOICES.get(current_state)
 | 
			
		||||
        current_choice = {ProcessStateEnum(current_state)}
 | 
			
		||||
        choices = (status_choices & role_choices) | current_choice
 | 
			
		||||
 | 
			
		||||
        # If user is owner of this process, we shall add the private choice if not existing, yet
 | 
			
		||||
        if is_owner:
 | 
			
		||||
            choices = {ProcessStateEnum.PRIVATE} | choices
 | 
			
		||||
 | 
			
		||||
        # Make sure enums are ordered by numerical value
 | 
			
		||||
        choices = sorted(choices, key=lambda _enum: _enum.value)
 | 
			
		||||
 | 
			
		||||
        # Create selectable and translated choices from enum list
 | 
			
		||||
        choices = [(enum.value, enum.name) for enum in choices]
 | 
			
		||||
        choices = ProcessStateEnum.__translate_choices(choices)
 | 
			
		||||
        return choices
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# DEFINES THE AVAILABLE STATES FOR EACH ROLE
 | 
			
		||||
PROCESS_ROLE_STATES = {
 | 
			
		||||
    RoleTypeEnum.DATAPROVIDER: {
 | 
			
		||||
        ProcessStateEnum.PRIVATE,
 | 
			
		||||
        ProcessStateEnum.ACCESSIBLE,
 | 
			
		||||
    },
 | 
			
		||||
    RoleTypeEnum.LICENSINGAUTHORITY: {
 | 
			
		||||
        #ProcessStateEnum.PRIVATE,
 | 
			
		||||
        ProcessStateEnum.ACCESSIBLE,
 | 
			
		||||
        ProcessStateEnum.LICENSED,
 | 
			
		||||
    },
 | 
			
		||||
    RoleTypeEnum.REGISTRATIONOFFICE: {
 | 
			
		||||
        #ProcessStateEnum.PRIVATE,
 | 
			
		||||
        ProcessStateEnum.ACCESSIBLE,
 | 
			
		||||
        ProcessStateEnum.OFFICIAL,
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# DEFINES POSSIBLE NEXT STATES FOR EACH PROCESS STATE
 | 
			
		||||
PROCESS_STATE_NEXT_CHOICES = {
 | 
			
		||||
    ProcessStateEnum.PRIVATE.value: {
 | 
			
		||||
        ProcessStateEnum.PRIVATE,
 | 
			
		||||
        ProcessStateEnum.ACCESSIBLE,
 | 
			
		||||
    },
 | 
			
		||||
    ProcessStateEnum.ACCESSIBLE.value: {
 | 
			
		||||
        ProcessStateEnum.PRIVATE,
 | 
			
		||||
        ProcessStateEnum.ACCESSIBLE,
 | 
			
		||||
        ProcessStateEnum.LICENSED,
 | 
			
		||||
    },
 | 
			
		||||
    ProcessStateEnum.LICENSED.value: {
 | 
			
		||||
        ProcessStateEnum.PRIVATE,
 | 
			
		||||
        ProcessStateEnum.ACCESSIBLE,
 | 
			
		||||
        ProcessStateEnum.LICENSED,
 | 
			
		||||
        ProcessStateEnum.OFFICIAL,
 | 
			
		||||
    },
 | 
			
		||||
    ProcessStateEnum.OFFICIAL.value: {
 | 
			
		||||
        ProcessStateEnum.ACCESSIBLE,
 | 
			
		||||
        ProcessStateEnum.OFFICIAL,
 | 
			
		||||
        ProcessStateEnum.RECORDED,
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# DEFINES FOR EACH STATE WHICH ROLE CAN EDIT THE PROCESS
 | 
			
		||||
PROCESS_EDITABLE_STATE = {
 | 
			
		||||
    ProcessStateEnum.PRIVATE.value: {
 | 
			
		||||
        RoleTypeEnum.DATAPROVIDER,
 | 
			
		||||
        RoleTypeEnum.LICENSINGAUTHORITY,
 | 
			
		||||
    },
 | 
			
		||||
    ProcessStateEnum.ACCESSIBLE.value: {
 | 
			
		||||
        RoleTypeEnum.LICENSINGAUTHORITY,
 | 
			
		||||
    },
 | 
			
		||||
    ProcessStateEnum.LICENSED.value: {
 | 
			
		||||
        RoleTypeEnum.REGISTRATIONOFFICE,
 | 
			
		||||
    },
 | 
			
		||||
    ProcessStateEnum.OFFICIAL.value: {
 | 
			
		||||
        RoleTypeEnum.REGISTRATIONOFFICE,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										218
									
								
								process/forms.py
									
									
									
									
									
								
							
							
						
						@ -1,218 +0,0 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: michel.peltriaux@sgdnord.rlp.de
 | 
			
		||||
Created on: 30.11.20
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
from dal import autocomplete
 | 
			
		||||
from django import forms
 | 
			
		||||
from django.contrib.auth.models import User
 | 
			
		||||
from django.db import transaction
 | 
			
		||||
from django.urls import reverse
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from intervention.models import Intervention
 | 
			
		||||
from konova.forms import BaseForm
 | 
			
		||||
from organisation.models import Organisation
 | 
			
		||||
from process.enums import ProcessStateEnum
 | 
			
		||||
from process.models import Process
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NewProcessForm(BaseForm):
 | 
			
		||||
    """
 | 
			
		||||
    A form for new process
 | 
			
		||||
    """
 | 
			
		||||
    identifier = forms.CharField(
 | 
			
		||||
        max_length=255,
 | 
			
		||||
        label=_("Identifier"),
 | 
			
		||||
        label_suffix=_(""),
 | 
			
		||||
        help_text=_("Generated automatically if none was given"),
 | 
			
		||||
        required=False,
 | 
			
		||||
    )
 | 
			
		||||
    title = forms.CharField(
 | 
			
		||||
        max_length=255,
 | 
			
		||||
        label=_("Title"),
 | 
			
		||||
        label_suffix=_(""),
 | 
			
		||||
        help_text=_("Proper title of the process"),
 | 
			
		||||
        required=True,
 | 
			
		||||
    )
 | 
			
		||||
    type = forms.CharField(
 | 
			
		||||
        max_length=255,
 | 
			
		||||
        label=_("Type"),
 | 
			
		||||
        label_suffix=_(""),
 | 
			
		||||
        help_text=_("Which process type is this"),
 | 
			
		||||
        required=True,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    licensing_authority = forms.ModelChoiceField(
 | 
			
		||||
        label=_("Licencing Authority"),
 | 
			
		||||
        label_suffix=_(""),
 | 
			
		||||
        required=True,
 | 
			
		||||
        queryset=Organisation.objects.all(),
 | 
			
		||||
        widget=autocomplete.ModelSelect2(
 | 
			
		||||
            url="orgs-autocomplete",
 | 
			
		||||
            attrs={
 | 
			
		||||
                "data-placeholder": _("Organization"),
 | 
			
		||||
                "data-minimum-input-length": 3,
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
    )
 | 
			
		||||
    licensing_authority_document_identifier = forms.CharField(
 | 
			
		||||
        max_length=255,
 | 
			
		||||
        label=_("Licencing document identifier"),
 | 
			
		||||
        label_suffix=_(""),
 | 
			
		||||
        required=True,
 | 
			
		||||
    )
 | 
			
		||||
    comment_licensing_authority = forms.CharField(
 | 
			
		||||
        widget=forms.Textarea,
 | 
			
		||||
        label=_("Comment licensing authority"),
 | 
			
		||||
        label_suffix=_(""),
 | 
			
		||||
        required=False,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    registration_office = forms.ModelChoiceField(
 | 
			
		||||
        label=_("Registration office"),
 | 
			
		||||
        label_suffix=_(""),
 | 
			
		||||
        required=True,
 | 
			
		||||
        queryset=Organisation.objects.all(),
 | 
			
		||||
        widget=autocomplete.ModelSelect2(
 | 
			
		||||
            url="orgs-autocomplete",
 | 
			
		||||
            attrs={
 | 
			
		||||
                "data-placeholder": _("Organization"),
 | 
			
		||||
                "data-minimum-input-length": 3,
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
    )
 | 
			
		||||
    registration_office_document_identifier = forms.CharField(
 | 
			
		||||
        max_length=255,
 | 
			
		||||
        label=_("Registration document identifier"),
 | 
			
		||||
        label_suffix=_(""),
 | 
			
		||||
        required=True,
 | 
			
		||||
    )
 | 
			
		||||
    comment_registration_office = forms.CharField(
 | 
			
		||||
        widget=forms.Textarea,
 | 
			
		||||
        label=_("Comment registration office"),
 | 
			
		||||
        label_suffix=_(""),
 | 
			
		||||
        required=False,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.action_url = reverse("process:new")
 | 
			
		||||
        self.cancel_redirect = reverse("process:index")
 | 
			
		||||
        self.form_title = _("Add new process")
 | 
			
		||||
        self.form_caption = _("Enter these basic information for the new process.")
 | 
			
		||||
 | 
			
		||||
    def save(self, user: User):
 | 
			
		||||
        """
 | 
			
		||||
        Persists process objects into database
 | 
			
		||||
        """
 | 
			
		||||
        with transaction.atomic():
 | 
			
		||||
            identifier = self.cleaned_data.get("identifier", None)
 | 
			
		||||
            title = self.cleaned_data.get("title", None)
 | 
			
		||||
            _type = self.cleaned_data.get("type", None)
 | 
			
		||||
            licencing_auth = self.cleaned_data.get("licensing_authority", None)
 | 
			
		||||
            licencing_auth_doc_id = self.cleaned_data.get("licensing_authority_document_identifier", None)
 | 
			
		||||
            reg_off = self.cleaned_data.get("registration_office", None)
 | 
			
		||||
            reg_off_doc_id = self.cleaned_data.get("registration_office_document_identifier", None)
 | 
			
		||||
            comment_license = self.cleaned_data.get("comment_licensing_authority", None)
 | 
			
		||||
            comment_registration = self.cleaned_data.get("comment_registration_office", None)
 | 
			
		||||
 | 
			
		||||
            process = Process()
 | 
			
		||||
            process.licensing_authority = licencing_auth
 | 
			
		||||
            process.licensing_authority_document_identifier = licencing_auth_doc_id
 | 
			
		||||
            process.registration_office = reg_off
 | 
			
		||||
            process.registration_office_document_identifier = reg_off_doc_id
 | 
			
		||||
            process.state = ProcessStateEnum.PRIVATE.value
 | 
			
		||||
            process.created_by = user
 | 
			
		||||
            process.licensing_authority_comment = comment_license
 | 
			
		||||
            process.registration_office_comment = comment_registration
 | 
			
		||||
            process.save()
 | 
			
		||||
 | 
			
		||||
            intervention = Intervention()
 | 
			
		||||
            intervention.title = title
 | 
			
		||||
            intervention.type = _type
 | 
			
		||||
            intervention.created_by = user
 | 
			
		||||
            intervention.process = process
 | 
			
		||||
            intervention.identifier = identifier
 | 
			
		||||
            intervention.save()
 | 
			
		||||
 | 
			
		||||
            return process
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EditProcessForm(NewProcessForm):
 | 
			
		||||
    status = forms.ChoiceField(
 | 
			
		||||
        label=_("Status"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        choices=[],
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        self.user = kwargs.pop("user", None)
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        if self.instance is not None:
 | 
			
		||||
            self.action_url = reverse("process:edit", args=(self.instance.id,))
 | 
			
		||||
        self.cancel_redirect = reverse("process:index")
 | 
			
		||||
        self.form_title = _("Edit process")
 | 
			
		||||
        self.form_caption = ""
 | 
			
		||||
        self.fields["status"].choices = ProcessStateEnum.as_next_role_choices(self.user, self.instance.state)
 | 
			
		||||
 | 
			
		||||
        # Initialize form data
 | 
			
		||||
        form_data = {
 | 
			
		||||
            "identifier": self.instance.intervention.identifier,
 | 
			
		||||
            "title": self.instance.intervention.title,
 | 
			
		||||
            "type": self.instance.intervention.type,
 | 
			
		||||
            "licensing_authority": self.instance.licensing_authority,
 | 
			
		||||
            "licensing_authority_document_identifier": self.instance.licensing_authority_document_identifier,
 | 
			
		||||
            "registration_office": self.instance.registration_office,
 | 
			
		||||
            "registration_office_document_identifier": self.instance.registration_office_document_identifier,
 | 
			
		||||
            "comment_licensing_authority": self.instance.licensing_authority_comment,
 | 
			
		||||
            "comment_registration_office": self.instance.registration_office_comment,
 | 
			
		||||
            "status": self.instance.state,
 | 
			
		||||
        }
 | 
			
		||||
        disabled_fields = [
 | 
			
		||||
            "identifier",
 | 
			
		||||
        ]
 | 
			
		||||
        self.load_initial_data(
 | 
			
		||||
            form_data,
 | 
			
		||||
            disabled_fields,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def save(self, user: User):
 | 
			
		||||
        """ Persists changes from form to instance
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            user (User): The performing user
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            process (Process): The edited process instance
 | 
			
		||||
        """
 | 
			
		||||
        with transaction.atomic():
 | 
			
		||||
            title = self.cleaned_data.get("title", None)
 | 
			
		||||
            _type = self.cleaned_data.get("type", None)
 | 
			
		||||
            licencing_auth = self.cleaned_data.get("licensing_authority", None)
 | 
			
		||||
            licencing_auth_doc_id = self.cleaned_data.get("licensing_authority_document_identifier", None)
 | 
			
		||||
            reg_off = self.cleaned_data.get("registration_office", None)
 | 
			
		||||
            reg_off_doc_id = self.cleaned_data.get("registration_office_document_identifier", None)
 | 
			
		||||
            comment_license = self.cleaned_data.get("comment_licensing_authority", None)
 | 
			
		||||
            comment_registration = self.cleaned_data.get("comment_registration_office", None)
 | 
			
		||||
            new_state = self.cleaned_data.get("status", None)
 | 
			
		||||
 | 
			
		||||
            self.instance.licensing_authority = licencing_auth
 | 
			
		||||
            self.instance.licensing_authority_document_identifier = licencing_auth_doc_id
 | 
			
		||||
            self.instance.registration_office = reg_off
 | 
			
		||||
            self.instance.registration_office_document_identifier = reg_off_doc_id
 | 
			
		||||
            self.instance.state = ProcessStateEnum.PRIVATE.value
 | 
			
		||||
            self.instance.created_by = user
 | 
			
		||||
            self.instance.licensing_authority_comment = comment_license
 | 
			
		||||
            self.instance.registration_office_comment = comment_registration
 | 
			
		||||
            self.instance.state = new_state
 | 
			
		||||
            self.instance.save()
 | 
			
		||||
 | 
			
		||||
            self.instance.intervention.title = title
 | 
			
		||||
            self.instance.intervention.type = _type
 | 
			
		||||
            self.instance.intervention.created_by = user
 | 
			
		||||
            self.instance.intervention.save()
 | 
			
		||||
 | 
			
		||||
            return self.instance
 | 
			
		||||
@ -1,158 +0,0 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: michel.peltriaux@sgdnord.rlp.de
 | 
			
		||||
Created on: 17.11.20
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
from django.contrib.auth.models import User
 | 
			
		||||
from django.db import models, transaction
 | 
			
		||||
 | 
			
		||||
from konova.models import BaseResource
 | 
			
		||||
from organisation.enums import RoleTypeEnum
 | 
			
		||||
from organisation.models import Organisation
 | 
			
		||||
from process.enums import ProcessStateEnum
 | 
			
		||||
from process.settings import PROCESS_STATE_STRINGS
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Process(BaseResource):
 | 
			
		||||
    """
 | 
			
		||||
    Process links compensation, intervention and eco accounts. These links are realized as ForeignKeys in their models.
 | 
			
		||||
 | 
			
		||||
    This process model therefore just holds further information on participated legal organizations.
 | 
			
		||||
 | 
			
		||||
    Attribute 'state' holds information on the current state of the process, which are defined in ProcessStateEnum
 | 
			
		||||
    """
 | 
			
		||||
    licensing_authority = models.ForeignKey(Organisation, on_delete=models.SET_NULL, null=True, blank=True, related_name="+")
 | 
			
		||||
    licensing_authority_document_identifier = models.CharField(max_length=500, null=True, blank=True)
 | 
			
		||||
    licensing_authority_comment = models.CharField(max_length=500, null=True, blank=True)
 | 
			
		||||
    registration_office_document_identifier = models.CharField(max_length=500, null=True, blank=True)
 | 
			
		||||
    registration_office = models.ForeignKey(Organisation, on_delete=models.SET_NULL, null=True, blank=True, related_name="+")
 | 
			
		||||
    registration_office_comment = models.CharField(max_length=500, null=True, blank=True)
 | 
			
		||||
    state = models.PositiveIntegerField(choices=ProcessStateEnum.as_choices(drop_empty_choice=True), default=0)
 | 
			
		||||
 | 
			
		||||
    def __str__(self) -> str:
 | 
			
		||||
        try:
 | 
			
		||||
            intervention = self.intervention
 | 
			
		||||
            title = intervention.title
 | 
			
		||||
        except AttributeError:
 | 
			
		||||
            title = "NO TITLE"
 | 
			
		||||
        return title
 | 
			
		||||
 | 
			
		||||
    def get_state_str(self):
 | 
			
		||||
        """
 | 
			
		||||
        Translates the numeric state into a string
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        return PROCESS_STATE_STRINGS.get(self.state, None)
 | 
			
		||||
 | 
			
		||||
    def deactivate(self):
 | 
			
		||||
        """ Deactivates a process and it's related elements
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        if self.is_active and not self.is_deleted:
 | 
			
		||||
            self.toggle_deletion()
 | 
			
		||||
 | 
			
		||||
    def activate(self):
 | 
			
		||||
        """ Activates a process and it's related elements
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        if not self.is_active and self.is_deleted:
 | 
			
		||||
            self.toggle_deletion()
 | 
			
		||||
 | 
			
		||||
    def toggle_deletion(self):
 | 
			
		||||
        """ Enables or disables a process
 | 
			
		||||
 | 
			
		||||
        Processes are not truly removed from the database, just toggled in their flags 'is_active' and 'is_deleted'
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        with transaction.atomic():
 | 
			
		||||
            self.is_active = not self.is_active
 | 
			
		||||
            self.is_deleted = not self.is_deleted
 | 
			
		||||
            self.save()
 | 
			
		||||
 | 
			
		||||
            # toggle related elements
 | 
			
		||||
            comps = self.compensation.all()
 | 
			
		||||
            elements = list(comps)
 | 
			
		||||
            if self.intervention is not None:
 | 
			
		||||
                elements.append(self.intervention)
 | 
			
		||||
 | 
			
		||||
            for elem in elements:
 | 
			
		||||
                elem.is_active = self.is_active
 | 
			
		||||
                elem.is_deleted = self.is_deleted
 | 
			
		||||
                elem.save()
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def get_role_objects(user: User, order_by: str = "-created_on"):
 | 
			
		||||
        """ Returns processes depending on the currently selected role of the user
 | 
			
		||||
 | 
			
		||||
            * REGISTRATIONOFFICE
 | 
			
		||||
                * User can see the processes where registration_office is set to the organisation of the currently selected role
 | 
			
		||||
                * User can see self-created processes
 | 
			
		||||
            * LICENSINGOFFICE
 | 
			
		||||
                * same
 | 
			
		||||
            * DATAPROVIDER
 | 
			
		||||
                * User can see only self-created processes
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            user (User): The performing user
 | 
			
		||||
            order_by (str): Order by which Process attribute
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        role = user.current_role
 | 
			
		||||
        if role is None:
 | 
			
		||||
            return Process.objects.none()
 | 
			
		||||
        _filter = {
 | 
			
		||||
            "is_deleted": False,
 | 
			
		||||
        }
 | 
			
		||||
        if role.role.type == RoleTypeEnum.REGISTRATIONOFFICE.value:
 | 
			
		||||
            _filter["registration_office"] = role.organisation
 | 
			
		||||
        elif role.role.type == RoleTypeEnum.LICENSINGAUTHORITY.value:
 | 
			
		||||
            _filter["licensing_authority"] = role.organisation
 | 
			
		||||
        elif role.role.type == RoleTypeEnum.DATAPROVIDER.value:
 | 
			
		||||
            # Nothing special
 | 
			
		||||
            _filter["created_by"] = user
 | 
			
		||||
        else:
 | 
			
		||||
            # None of the above
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        other_processes = Process.objects.filter(
 | 
			
		||||
            **_filter
 | 
			
		||||
        )
 | 
			
		||||
        user_created_processes = Process.objects.filter(
 | 
			
		||||
            created_by=user,
 | 
			
		||||
            is_deleted=False,
 | 
			
		||||
        )
 | 
			
		||||
        qs = (other_processes | user_created_processes).distinct()
 | 
			
		||||
        qs = qs.order_by(order_by)
 | 
			
		||||
        return qs
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def create_from_intervention(intervention):
 | 
			
		||||
        """ Creates a process for an intervention, in case an intervention has been created without a process
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            intervention (Intervention): The intervention
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            process (Process)
 | 
			
		||||
        """
 | 
			
		||||
        process = Process()
 | 
			
		||||
        process.identifier = intervention.identifier
 | 
			
		||||
        process.title = intervention.title
 | 
			
		||||
        process.created_by = intervention.created_by
 | 
			
		||||
        process.save()
 | 
			
		||||
 | 
			
		||||
        intervention.process = process
 | 
			
		||||
        intervention.save()
 | 
			
		||||
        return process
 | 
			
		||||
@ -1,19 +0,0 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: michel.peltriaux@sgdnord.rlp.de
 | 
			
		||||
Created on: 25.11.20
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from process.enums import ProcessStateEnum
 | 
			
		||||
 | 
			
		||||
# DEFINES TRANSLATIONS FOR EACH PROCESSSTATEENUM
 | 
			
		||||
PROCESS_STATE_STRINGS = {
 | 
			
		||||
    ProcessStateEnum.PRIVATE.value: _("private"),
 | 
			
		||||
    ProcessStateEnum.ACCESSIBLE.value: _("accessible"),
 | 
			
		||||
    ProcessStateEnum.LICENSED.value: _("licensed"),
 | 
			
		||||
    ProcessStateEnum.OFFICIAL.value: _("official"),
 | 
			
		||||
    ProcessStateEnum.RECORDED.value: _("recorded"),
 | 
			
		||||
}
 | 
			
		||||
@ -1,130 +0,0 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: michel.peltriaux@sgdnord.rlp.de
 | 
			
		||||
Created on: 25.11.20
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
from django.template.loader import render_to_string
 | 
			
		||||
from django.urls import reverse
 | 
			
		||||
from django.utils.html import format_html
 | 
			
		||||
 | 
			
		||||
from konova.utils.tables import BaseTable, ChoicesColumnForm
 | 
			
		||||
import django_tables2 as tables
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from process.enums import ProcessStateEnum
 | 
			
		||||
from process.models import Process
 | 
			
		||||
from process.settings import PROCESS_STATE_STRINGS
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ProcessTable(BaseTable):
 | 
			
		||||
    id = tables.Column(
 | 
			
		||||
        verbose_name=_("Intervention identifier"),
 | 
			
		||||
        orderable=True,
 | 
			
		||||
        accessor="intervention.identifier",
 | 
			
		||||
    )
 | 
			
		||||
    t = tables.Column(
 | 
			
		||||
        verbose_name=_("Title"),
 | 
			
		||||
        orderable=True,
 | 
			
		||||
        accessor="intervention.title",
 | 
			
		||||
    )
 | 
			
		||||
    """
 | 
			
		||||
    THESE COLUMNS MIGHT NOT BE OF INTEREST. TO REDUCE TABLE WIDTH THEY CAN BE REMOVED
 | 
			
		||||
    
 | 
			
		||||
    dila = tables.Column(
 | 
			
		||||
        verbose_name=_("Licensing authority document identifier"),
 | 
			
		||||
        orderable=True,
 | 
			
		||||
        accessor="licensing_authority_document_identifier",
 | 
			
		||||
    )
 | 
			
		||||
    diro = tables.Column(
 | 
			
		||||
        verbose_name=_("Registration office document identifier"),
 | 
			
		||||
        orderable=True,
 | 
			
		||||
        accessor="registration_office_document_identifier",
 | 
			
		||||
    )
 | 
			
		||||
    """
 | 
			
		||||
    s = tables.Column(
 | 
			
		||||
        verbose_name=_("Status"),
 | 
			
		||||
        orderable=True,
 | 
			
		||||
        accessor="state",
 | 
			
		||||
    )
 | 
			
		||||
    d = tables.Column(
 | 
			
		||||
        verbose_name=_("Created on"),
 | 
			
		||||
        orderable=True,
 | 
			
		||||
        accessor="created_on",
 | 
			
		||||
    )
 | 
			
		||||
    ac = tables.Column(
 | 
			
		||||
        verbose_name=_("Actions"),
 | 
			
		||||
        orderable=False,
 | 
			
		||||
        empty_values=[],
 | 
			
		||||
        attrs={"td": {"class": "action-col"}}
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.title = _("Processes")
 | 
			
		||||
        self.add_new_url = reverse("process:new")
 | 
			
		||||
 | 
			
		||||
    def render_id(self, value, record: Process):
 | 
			
		||||
        """ Renders the id column for an intervention
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            value (str): The identifier value
 | 
			
		||||
            record (Process): The process record
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        html = ""
 | 
			
		||||
        html += self.render_link(
 | 
			
		||||
            tooltip=_("Open {}").format(_("Process")),
 | 
			
		||||
            href=reverse("process:open", args=(record.id,)),
 | 
			
		||||
            txt=value,
 | 
			
		||||
            new_tab=False,
 | 
			
		||||
        )
 | 
			
		||||
        return format_html(html)
 | 
			
		||||
 | 
			
		||||
    def render_s(self, value, record, column) -> str:
 | 
			
		||||
        """ Translates the record state to a desired language
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            value (str): The value of state inside record
 | 
			
		||||
            record (Process): The whole record itself
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            str
 | 
			
		||||
        """
 | 
			
		||||
        state = record.state
 | 
			
		||||
        is_owner = record.created_by == self.request.user
 | 
			
		||||
        choices = ProcessStateEnum.as_next_role_choices(self.request.user, state, is_owner)
 | 
			
		||||
        valid_choices = [choice[0] for choice in choices]
 | 
			
		||||
        if state in valid_choices:
 | 
			
		||||
            form = ChoicesColumnForm(
 | 
			
		||||
                action_url=reverse("process:edit-status", args=(record.id,)),
 | 
			
		||||
                choices=choices,
 | 
			
		||||
                initial={"select": state},
 | 
			
		||||
            )
 | 
			
		||||
            rendered = render_to_string("konova/choiceColumnForm.html", context={"form": form}, request=self.request)
 | 
			
		||||
        else:
 | 
			
		||||
            rendered = PROCESS_STATE_STRINGS.get(state)
 | 
			
		||||
        return rendered
 | 
			
		||||
 | 
			
		||||
    def render_ac(self, value, record):
 | 
			
		||||
        """
 | 
			
		||||
        Renders possible actions for this record, such as delete.
 | 
			
		||||
        """
 | 
			
		||||
        process = _("Process")
 | 
			
		||||
        html = ""
 | 
			
		||||
        html += self.render_open_btn(
 | 
			
		||||
            _("Open {}").format(process),
 | 
			
		||||
            reverse("process:open", args=(record.id,)),
 | 
			
		||||
        )
 | 
			
		||||
        html += self.render_edit_btn(
 | 
			
		||||
            _("Edit {}").format(process),
 | 
			
		||||
            reverse("process:edit", args=(record.id,)),
 | 
			
		||||
        )
 | 
			
		||||
        html += self.render_delete_btn(
 | 
			
		||||
            _("Delete {}").format(process),
 | 
			
		||||
            reverse("process:remove", args=(record.id,)),
 | 
			
		||||
        )
 | 
			
		||||
        return format_html(html)
 | 
			
		||||
@ -1,95 +0,0 @@
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n static custom_tags %}
 | 
			
		||||
 | 
			
		||||
{% block body %}
 | 
			
		||||
    <div class="rows">
 | 
			
		||||
        <div class="columns">
 | 
			
		||||
            <div class="large-10 column">
 | 
			
		||||
                <h3>{% trans 'Process' %}: {{process.intervention.identifier}}</h3>
 | 
			
		||||
                <h4>{{process.intervention.title}}</h4>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="large-2 column">
 | 
			
		||||
                <a href="{% url 'process:edit' process.id %}">
 | 
			
		||||
                    <button class="button small" role="button" value="{% trans 'Edit' %}"><em class='fas fa-edit'></em> {% trans 'Edit' %}</button>
 | 
			
		||||
                </a>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="rows">
 | 
			
		||||
 | 
			
		||||
        <table>
 | 
			
		||||
            <tbody>
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <th scope="row">{% trans 'Licencing Authority' %}</th>
 | 
			
		||||
                    <td>{{ process.licensing_authority }}</td>
 | 
			
		||||
                </tr>
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <th scope="row">{% trans 'Licencing document identifier' %}</th>
 | 
			
		||||
                    <td>{{ process.licensing_authority_document_identifier }}</td>
 | 
			
		||||
                </tr>
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <th scope="row">{% trans 'Registration office' %}</th>
 | 
			
		||||
                    <td>{{ process.registration_office }}</td>
 | 
			
		||||
                </tr>
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <th scope="row">{% trans 'Registration document identifier' %}</th>
 | 
			
		||||
                    <td>{{ process.registration_office_document_identifier }}</td>
 | 
			
		||||
                </tr>
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <th scope="row">{% trans 'Status' %}</th>
 | 
			
		||||
                    <td>
 | 
			
		||||
                        <span class="small info" title="{% trans 'Status' %}">
 | 
			
		||||
                            <em class="fas fa-exclamation-triangle"></em>
 | 
			
		||||
                            {{process.state|resolve_process_state}}
 | 
			
		||||
                        </span>
 | 
			
		||||
                    </td>
 | 
			
		||||
                </tr>
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <th scope="row">{% trans 'Intervention' %}</th>
 | 
			
		||||
                    <td>
 | 
			
		||||
                        <a href="{% url 'intervention:open' process.intervention.id %}" target="_blank">
 | 
			
		||||
                            <button class="button small" role="button" title="{{process.intervention.title}} - {{process.intervention.type}}">{{process.intervention.identifier}}</button>
 | 
			
		||||
                        </a>
 | 
			
		||||
                    </td>
 | 
			
		||||
                </tr>
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <th scope="row" style="vertical-align: baseline">
 | 
			
		||||
                        {% trans 'Compensations' %}
 | 
			
		||||
                        <br>
 | 
			
		||||
                        <a href="{% url 'process:add-compensation' process.id %}" target="_blank">
 | 
			
		||||
                            <button class="button small" role="button" title="{% trans 'Add a new compensation' %}">
 | 
			
		||||
                                <em class="fas fa-plus-circle"></em>
 | 
			
		||||
                                {% trans 'New' %}
 | 
			
		||||
                            </button>
 | 
			
		||||
                        </a>
 | 
			
		||||
                    </th>
 | 
			
		||||
                    <td>
 | 
			
		||||
                        <table>
 | 
			
		||||
                            <tbody>
 | 
			
		||||
                                {% for compensation in process.compensations.all %}
 | 
			
		||||
                                <tr>
 | 
			
		||||
                                    <td>
 | 
			
		||||
                                        <a href="{% url 'compensation:open' compensation.id %}" target="_blank">
 | 
			
		||||
                                            <button class="button small" role="button" title="{{compensation.title}} - {{compensation.type}}">{{compensation.identifier}}</button>
 | 
			
		||||
                                        </a>
 | 
			
		||||
                                    </td>
 | 
			
		||||
                                    <td class="action-col">
 | 
			
		||||
                                        <a href="{% url 'compensation:edit' compensation.id %}" title="{% trans 'Edit' %}"><button class="button small" title="{% trans 'Edit' %}"><em class='fas fa-edit'></em></button></a>
 | 
			
		||||
                                        <a href="{% url 'compensation:remove' compensation.id %}" title="{% trans 'Remove' %}"><button class="button small" title="{% trans 'Remove' %}"><em class='fas fa-trash-alt'></em></button></a>
 | 
			
		||||
                                    </td>
 | 
			
		||||
                                </tr>
 | 
			
		||||
                                {% empty %}
 | 
			
		||||
                                <tr>
 | 
			
		||||
                                    <td>
 | 
			
		||||
                                        {% trans 'No compensation' %}
 | 
			
		||||
                                    </td>
 | 
			
		||||
                                </tr>
 | 
			
		||||
                                {% endfor %}
 | 
			
		||||
                            </tbody>
 | 
			
		||||
                        </table>
 | 
			
		||||
                    </td>
 | 
			
		||||
                </tr>
 | 
			
		||||
            </tbody>
 | 
			
		||||
        </table>
 | 
			
		||||
    </div>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
@ -1,3 +0,0 @@
 | 
			
		||||
from django.test import TestCase
 | 
			
		||||
 | 
			
		||||
# Create your tests here.
 | 
			
		||||
@ -1,22 +0,0 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: michel.peltriaux@sgdnord.rlp.de
 | 
			
		||||
Created on: 25.11.20
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
from django.urls import path
 | 
			
		||||
 | 
			
		||||
from process.views import index_view, new_view, open_view, edit_view, remove_view, edit_status_view, \
 | 
			
		||||
    add_compensation_view
 | 
			
		||||
 | 
			
		||||
app_name = "process"
 | 
			
		||||
urlpatterns = [
 | 
			
		||||
    path('', index_view, name='index'),
 | 
			
		||||
    path('new/', new_view, name='new'),
 | 
			
		||||
    path('open/<id>', open_view, name='open'),
 | 
			
		||||
    path('edit/<id>', edit_view, name='edit'),
 | 
			
		||||
    path('edit/<id>/status', edit_status_view, name='edit-status'),
 | 
			
		||||
    path('remove/<id>', remove_view, name='remove'),
 | 
			
		||||
    path('ac/<id>', add_compensation_view, name='add-compensation'),
 | 
			
		||||
]
 | 
			
		||||
							
								
								
									
										217
									
								
								process/views.py
									
									
									
									
									
								
							
							
						
						@ -1,217 +0,0 @@
 | 
			
		||||
from django.contrib import messages
 | 
			
		||||
from django.contrib.auth.decorators import login_required
 | 
			
		||||
from django.http import HttpRequest
 | 
			
		||||
from django.shortcuts import render, redirect, get_object_or_404
 | 
			
		||||
from django.urls import reverse
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from compensation.models import Compensation
 | 
			
		||||
from konova.contexts import BaseContext
 | 
			
		||||
from konova.decorators import resolve_user_role, valid_process_role_required
 | 
			
		||||
from konova.forms import RemoveForm
 | 
			
		||||
from konova.utils.tables import ChoicesColumnForm
 | 
			
		||||
from process.enums import ProcessStateEnum
 | 
			
		||||
from process.forms import NewProcessForm, EditProcessForm
 | 
			
		||||
from process.models import Process
 | 
			
		||||
from process.settings import PROCESS_STATE_STRINGS
 | 
			
		||||
from process.tables import ProcessTable
 | 
			
		||||
 | 
			
		||||
# URL definitions
 | 
			
		||||
PROCESS_INDEX_URL = "process:index"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@login_required
 | 
			
		||||
@resolve_user_role
 | 
			
		||||
def index_view(request: HttpRequest):
 | 
			
		||||
    """
 | 
			
		||||
    Renders the index view for process
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        request (HttpRequest): The incoming request
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
        A rendered view
 | 
			
		||||
    """
 | 
			
		||||
    template = "generic_index.html"
 | 
			
		||||
    user = request.user
 | 
			
		||||
    processes = Process.get_role_objects(user)
 | 
			
		||||
    table = ProcessTable(
 | 
			
		||||
        request=request,
 | 
			
		||||
        queryset=processes
 | 
			
		||||
    )
 | 
			
		||||
    context = {
 | 
			
		||||
        "table": table,
 | 
			
		||||
    }
 | 
			
		||||
    context = BaseContext(request, context).context
 | 
			
		||||
    return render(request, template, context)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@login_required
 | 
			
		||||
def new_view(request: HttpRequest):
 | 
			
		||||
    """
 | 
			
		||||
    Renders a view for a new process creation
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        request (HttpRequest): The incoming request
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    template = "konova/form.html"
 | 
			
		||||
    form = NewProcessForm(request.POST or None)
 | 
			
		||||
    if request.method == "POST":
 | 
			
		||||
        if form.is_valid():
 | 
			
		||||
            process = form.save(request.user)
 | 
			
		||||
            intervention = process.intervention
 | 
			
		||||
            messages.info(request, _("A process is based on an intervention. Please fill in the missing data for this intervention"))
 | 
			
		||||
            return redirect("intervention:edit", id=intervention.id)
 | 
			
		||||
        else:
 | 
			
		||||
            messages.error(request, _("Invalid input"))
 | 
			
		||||
    else:
 | 
			
		||||
        # For clarification: Nothing to do here
 | 
			
		||||
        pass
 | 
			
		||||
    context = {
 | 
			
		||||
        "form": form,
 | 
			
		||||
    }
 | 
			
		||||
    context = BaseContext(request, context).context
 | 
			
		||||
    return render(request, template, context)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@login_required
 | 
			
		||||
def open_view(request: HttpRequest, id: str):
 | 
			
		||||
    """ Renders a detail view for this process
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        request (HttpRequest): The incoming request
 | 
			
		||||
        id (str): The uuid id as string
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    template = "process/open.html"
 | 
			
		||||
    process = get_object_or_404(Process, id=id)
 | 
			
		||||
    context = {
 | 
			
		||||
        "process": process,
 | 
			
		||||
    }
 | 
			
		||||
    context = BaseContext(request, context).context
 | 
			
		||||
    return render(request, template, context)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@login_required
 | 
			
		||||
@resolve_user_role
 | 
			
		||||
def edit_view(request: HttpRequest, id: str):
 | 
			
		||||
    """ Renders an edit view for this process
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        request (HttpRequest): The incoming request
 | 
			
		||||
        id (str): The uuid id as string
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    template = "konova/form.html"
 | 
			
		||||
    process = get_object_or_404(Process, id=id)
 | 
			
		||||
    if request.method == "POST":
 | 
			
		||||
        form = EditProcessForm(request.POST or None, instance=process, user=request.user)
 | 
			
		||||
        if form.is_valid():
 | 
			
		||||
            process = form.save(request.user)
 | 
			
		||||
            messages.success(request, _("{} edited").format(process))
 | 
			
		||||
            return redirect("process:index")
 | 
			
		||||
        else:
 | 
			
		||||
            messages.error(request, _("Invalid input"))
 | 
			
		||||
    form = EditProcessForm(instance=process, user=request.user)
 | 
			
		||||
    context = {
 | 
			
		||||
        "form": form,
 | 
			
		||||
    }
 | 
			
		||||
    context = BaseContext(request, context).context
 | 
			
		||||
    return render(request, template, context)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@login_required
 | 
			
		||||
def remove_view(request: HttpRequest, id: str):
 | 
			
		||||
    """ Renders a remove view for this process
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        request (HttpRequest): The incoming request
 | 
			
		||||
        id (str): The uuid id as string
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    template = "konova/form.html"
 | 
			
		||||
    process = get_object_or_404(Process, id=id)
 | 
			
		||||
    if request.method == "POST":
 | 
			
		||||
        form = RemoveForm(
 | 
			
		||||
            request.POST or None,
 | 
			
		||||
            object_to_remove=process,
 | 
			
		||||
            remove_post_url=reverse("process:remove", args=(id,)),
 | 
			
		||||
            cancel_url=reverse("process:index"),
 | 
			
		||||
        )
 | 
			
		||||
        if form.is_valid():
 | 
			
		||||
            confirmed = form.is_checked()
 | 
			
		||||
            if confirmed:
 | 
			
		||||
                process.deactivate()
 | 
			
		||||
                messages.success(request, _("Process {} removed").format(process))
 | 
			
		||||
            return redirect("process:index")
 | 
			
		||||
        else:
 | 
			
		||||
            messages.error(request, _("Invalid input"))
 | 
			
		||||
 | 
			
		||||
    form = RemoveForm(
 | 
			
		||||
        object_to_remove=process,
 | 
			
		||||
        remove_post_url=reverse("process:remove", args=(id,)),
 | 
			
		||||
        cancel_url=reverse("process:index"),
 | 
			
		||||
    )
 | 
			
		||||
    context = {
 | 
			
		||||
        "form": form,
 | 
			
		||||
    }
 | 
			
		||||
    context = BaseContext(request, context).context
 | 
			
		||||
    return render(request, template, context)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@login_required
 | 
			
		||||
def edit_status_view(request: HttpRequest, id: str):
 | 
			
		||||
    """ Changes only the status of a process
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        request (The incoming request):
 | 
			
		||||
        id ():
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    process = get_object_or_404(Process, id=id)
 | 
			
		||||
    old_state = process.state
 | 
			
		||||
    form = ChoicesColumnForm(request.POST or None, choices=ProcessStateEnum.as_choices(drop_empty_choice=True))
 | 
			
		||||
    if request.method == "POST":
 | 
			
		||||
        if form.is_valid():
 | 
			
		||||
            process.state = int(form.cleaned_data.get("select", -1))
 | 
			
		||||
            process.save()
 | 
			
		||||
            _from = PROCESS_STATE_STRINGS.get(old_state)
 | 
			
		||||
            _to = PROCESS_STATE_STRINGS.get(process.state)
 | 
			
		||||
            messages.info(request, _("{} status changed from {} to {}").format(process.intervention.title, _from, _to))
 | 
			
		||||
        else:
 | 
			
		||||
            messages.error(request, form.errors)
 | 
			
		||||
    return redirect("process:index")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@login_required
 | 
			
		||||
def add_compensation_view(request: HttpRequest, id: str):
 | 
			
		||||
    """ Adds a new compensation to a process
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        request (HttpRequest): The incoming request
 | 
			
		||||
        id (str): The process' id
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    process = get_object_or_404(Process, id=id)
 | 
			
		||||
    comp = Compensation()
 | 
			
		||||
    comp.process = process
 | 
			
		||||
    comp.save()
 | 
			
		||||
    messages.info(request, _("Please fill in the data for this compensation"))
 | 
			
		||||
    return redirect("compensation:edit", id=comp.id)
 | 
			
		||||
    #template = ""
 | 
			
		||||
    #context = {}
 | 
			
		||||
    #context = BaseContext(request, context).context
 | 
			
		||||
    #return render(request, template, context)
 | 
			
		||||
@ -1,20 +1,25 @@
 | 
			
		||||
asgiref==3.3.1
 | 
			
		||||
beautifulsoup4==4.9.3
 | 
			
		||||
certifi==2020.11.8
 | 
			
		||||
chardet==3.0.4
 | 
			
		||||
Django==3.1.3
 | 
			
		||||
django-autocomplete-light==3.8.1
 | 
			
		||||
django-bootstrap4==3.0.1
 | 
			
		||||
django-debug-toolbar==3.1.1
 | 
			
		||||
django-filter==2.4.0
 | 
			
		||||
django-fontawesome-5==1.0.18
 | 
			
		||||
django-simple-sso==0.14.1
 | 
			
		||||
django-tables2==2.3.3
 | 
			
		||||
idna==2.10
 | 
			
		||||
importlib-metadata==2.1.1
 | 
			
		||||
itsdangerous==1.1.0
 | 
			
		||||
pkg-resources==0.0.0
 | 
			
		||||
psycopg2==2.8.6
 | 
			
		||||
pytz==2020.4
 | 
			
		||||
requests==2.25.0
 | 
			
		||||
six==1.15.0
 | 
			
		||||
soupsieve==2.2.1
 | 
			
		||||
sqlparse==0.4.1
 | 
			
		||||
urllib3==1.26.2
 | 
			
		||||
webservices==0.7
 | 
			
		||||
zipp==3.4.1
 | 
			
		||||
 | 
			
		||||
@ -4,12 +4,8 @@
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="UTF-8">
 | 
			
		||||
    <title>{{ base_title }}</title>
 | 
			
		||||
    <link rel="stylesheet" href="{% static 'css/messages.css' %}">
 | 
			
		||||
    <link rel="stylesheet" href="{% static 'css/mulewf.css' %}">
 | 
			
		||||
    <link rel="stylesheet" href="{% static 'css/konova.css' %}">
 | 
			
		||||
    <script type="text/javascript" src="{% static 'js/jquery-3.5.1.min.js' %}"></script>
 | 
			
		||||
    <script type="text/javascript" src="{% static 'js/jquery-ui.min.js' %}"></script>
 | 
			
		||||
    <script type="text/javascript" src="{% static 'js/mulewf.min.js' %}"></script>
 | 
			
		||||
    {% bootstrap_css %}
 | 
			
		||||
    {% bootstrap_javascript jquery='full' %}
 | 
			
		||||
    {% fontawesome_5_static %}
 | 
			
		||||
    {% block head %}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||