Initial
This commit is contained in:
		
						commit
						c14e9466fb
					
				
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					# Project exclude paths
 | 
				
			||||||
 | 
					/venv/
 | 
				
			||||||
 | 
					/.idea/
 | 
				
			||||||
 | 
					*/migrations/
 | 
				
			||||||
							
								
								
									
										0
									
								
								compensation/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								compensation/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										46
									
								
								compensation/admin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								compensation/admin.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					from django.contrib import admin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from compensation.models import Compensation, CompensationAction, CompensationState, CompensationControl
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CompensationControlAdmin(admin.ModelAdmin):
 | 
				
			||||||
 | 
					    list_display = [
 | 
				
			||||||
 | 
					        "id",
 | 
				
			||||||
 | 
					        "type",
 | 
				
			||||||
 | 
					        "deadline",
 | 
				
			||||||
 | 
					        "expected_result",
 | 
				
			||||||
 | 
					        "by_authority",
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CompensationStateAdmin(admin.ModelAdmin):
 | 
				
			||||||
 | 
					    list_display = [
 | 
				
			||||||
 | 
					        "id",
 | 
				
			||||||
 | 
					        "biotope_type",
 | 
				
			||||||
 | 
					        "amount",
 | 
				
			||||||
 | 
					        "unit",
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CompensationActionAdmin(admin.ModelAdmin):
 | 
				
			||||||
 | 
					    list_display = [
 | 
				
			||||||
 | 
					        "id",
 | 
				
			||||||
 | 
					        "action_type",
 | 
				
			||||||
 | 
					        "amount",
 | 
				
			||||||
 | 
					        "unit",
 | 
				
			||||||
 | 
					        "control",
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CompensationAdmin(admin.ModelAdmin):
 | 
				
			||||||
 | 
					    list_display = [
 | 
				
			||||||
 | 
					        "id",
 | 
				
			||||||
 | 
					        "type",
 | 
				
			||||||
 | 
					        "created_on",
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					admin.site.register(Compensation, CompensationAdmin)
 | 
				
			||||||
 | 
					admin.site.register(CompensationAction, CompensationActionAdmin)
 | 
				
			||||||
 | 
					admin.site.register(CompensationState, CompensationStateAdmin)
 | 
				
			||||||
 | 
					admin.site.register(CompensationControl, CompensationControlAdmin)
 | 
				
			||||||
							
								
								
									
										5
									
								
								compensation/apps.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								compensation/apps.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					from django.apps import AppConfig
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CompensationConfig(AppConfig):
 | 
				
			||||||
 | 
					    name = 'compensation'
 | 
				
			||||||
							
								
								
									
										14
									
								
								compensation/forms.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								compensation/forms.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Author: Michel Peltriaux
 | 
				
			||||||
 | 
					Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
				
			||||||
 | 
					Contact: michel.peltriaux@sgdnord.rlp.de
 | 
				
			||||||
 | 
					Created on: 04.12.20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					from konova.forms import BaseForm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class NewCompensationForm(BaseForm):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, *args, **kwargs):
 | 
				
			||||||
 | 
					        super().__init__(*args, **kwargs)
 | 
				
			||||||
							
								
								
									
										145
									
								
								compensation/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								compensation/models.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,145 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					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.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.utils.generators import generate_random_string
 | 
				
			||||||
 | 
					from process.models import Process
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CompensationControl(BaseResource):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Holds data on how a compensation shall be controlled
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    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)
 | 
				
			||||||
 | 
					    comment = models.TextField()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CompensationAction(BaseResource):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Compensations include actions like planting trees, refreshing rivers and so on.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    action_type = models.CharField(max_length=500, null=True, blank=True)
 | 
				
			||||||
 | 
					    amount = models.FloatField()
 | 
				
			||||||
 | 
					    unit = models.CharField(max_length=100, null=True, blank=True)
 | 
				
			||||||
 | 
					    control = models.ForeignKey(CompensationControl, on_delete=models.SET_NULL, null=True, blank=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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)
 | 
				
			||||||
 | 
					    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!
 | 
				
			||||||
 | 
					    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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    def __generate_new_identifier() -> str:
 | 
				
			||||||
 | 
					        """ Generates a new identifier for the intervention object
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					            str
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        curr_month = str(now().month)
 | 
				
			||||||
 | 
					        curr_year = str(now().year)
 | 
				
			||||||
 | 
					        rand_str = generate_random_string(
 | 
				
			||||||
 | 
					            length=COMPENSATION_IDENTIFIER_LENGTH,
 | 
				
			||||||
 | 
					            only_numbers=True,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        _str = "{}{}{}".format(curr_month, curr_year, rand_str)
 | 
				
			||||||
 | 
					        return COMPENSATION_IDENTIFIER_TEMPLATE.format(_str)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def save(self, *args, **kwargs):
 | 
				
			||||||
 | 
					        if self.identifier is None or len(self.identifier) == 0:
 | 
				
			||||||
 | 
					            # Create new identifier
 | 
				
			||||||
 | 
					            new_id = self.__generate_new_identifier()
 | 
				
			||||||
 | 
					            while Compensation.objects.filter(identifier=new_id).exists():
 | 
				
			||||||
 | 
					                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"):
 | 
				
			||||||
 | 
					        """ 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):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    '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()
 | 
				
			||||||
							
								
								
									
										9
									
								
								compensation/settings.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								compensation/settings.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Author: Michel Peltriaux
 | 
				
			||||||
 | 
					Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
				
			||||||
 | 
					Contact: michel.peltriaux@sgdnord.rlp.de
 | 
				
			||||||
 | 
					Created on: 18.12.20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					COMPENSATION_IDENTIFIER_LENGTH = 10
 | 
				
			||||||
 | 
					COMPENSATION_IDENTIFIER_TEMPLATE = "KOM-{}"
 | 
				
			||||||
							
								
								
									
										118
									
								
								compensation/tables.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								compensation/tables.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,118 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Author: Michel Peltriaux
 | 
				
			||||||
 | 
					Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
				
			||||||
 | 
					Contact: michel.peltriaux@sgdnord.rlp.de
 | 
				
			||||||
 | 
					Created on: 01.12.20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					from django.urls import reverse
 | 
				
			||||||
 | 
					from django.utils.html import format_html
 | 
				
			||||||
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from konova.utils.tables import BaseTable
 | 
				
			||||||
 | 
					import django_tables2 as tables
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CompensationTable(BaseTable):
 | 
				
			||||||
 | 
					    id = tables.Column(
 | 
				
			||||||
 | 
					        verbose_name=_("Identifier"),
 | 
				
			||||||
 | 
					        orderable=True,
 | 
				
			||||||
 | 
					        accessor="identifier",
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    t = tables.Column(
 | 
				
			||||||
 | 
					        verbose_name=_("Title"),
 | 
				
			||||||
 | 
					        orderable=True,
 | 
				
			||||||
 | 
					        accessor="title",
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    p = tables.Column(
 | 
				
			||||||
 | 
					        verbose_name=_("Process"),
 | 
				
			||||||
 | 
					        orderable=True,
 | 
				
			||||||
 | 
					        accessor="process",
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    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 = _("Compensations")
 | 
				
			||||||
 | 
					        self.add_new_url = reverse("compensation:new")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def render_ac(self, value, record):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Renders possible actions for this record, such as delete.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        intervention = _("Compensation")
 | 
				
			||||||
 | 
					        html = ""
 | 
				
			||||||
 | 
					        html += self.render_open_btn(
 | 
				
			||||||
 | 
					            _("Open {}").format(intervention),
 | 
				
			||||||
 | 
					            reverse("compensation:open", args=(record.id,)),
 | 
				
			||||||
 | 
					            new_tab=True
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        html += self.render_edit_btn(
 | 
				
			||||||
 | 
					            _("Edit {}").format(intervention),
 | 
				
			||||||
 | 
					            reverse("compensation:edit", args=(record.id,)),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        html += self.render_delete_btn(
 | 
				
			||||||
 | 
					            _("Delete {}").format(intervention),
 | 
				
			||||||
 | 
					            reverse("compensation:remove", args=(record.id,)),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        return format_html(html)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class EcoAccountTable(BaseTable):
 | 
				
			||||||
 | 
					    id = tables.Column(
 | 
				
			||||||
 | 
					        verbose_name=_("Identifier"),
 | 
				
			||||||
 | 
					        orderable=True,
 | 
				
			||||||
 | 
					        accessor="identifier",
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    t = tables.Column(
 | 
				
			||||||
 | 
					        verbose_name=_("Title"),
 | 
				
			||||||
 | 
					        orderable=True,
 | 
				
			||||||
 | 
					        accessor="title",
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    d = tables.Column(
 | 
				
			||||||
 | 
					        verbose_name=_("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 = _("Eco Accounts")
 | 
				
			||||||
 | 
					        self.add_new_url = reverse("compensation:account-new")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def render_ac(self, value, record):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Renders possible actions for this record, such as delete.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        intervention = _("Compensation")
 | 
				
			||||||
 | 
					        html = ""
 | 
				
			||||||
 | 
					        html += self.render_open_btn(
 | 
				
			||||||
 | 
					            _("Open {}").format(intervention),
 | 
				
			||||||
 | 
					            reverse("compensation:open", args=(record.id,)),
 | 
				
			||||||
 | 
					            new_tab=True
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        html += self.render_edit_btn(
 | 
				
			||||||
 | 
					            _("Edit {}").format(intervention),
 | 
				
			||||||
 | 
					            reverse("compensation:edit", args=(record.id,)),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        html += self.render_delete_btn(
 | 
				
			||||||
 | 
					            _("Delete {}").format(intervention),
 | 
				
			||||||
 | 
					            reverse("compensation:remove", args=(record.id,)),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        return format_html(html)
 | 
				
			||||||
							
								
								
									
										3
									
								
								compensation/tests.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								compensation/tests.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					from django.test import TestCase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Create your tests here.
 | 
				
			||||||
							
								
								
									
										27
									
								
								compensation/urls.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								compensation/urls.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Author: Michel Peltriaux
 | 
				
			||||||
 | 
					Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
				
			||||||
 | 
					Contact: michel.peltriaux@sgdnord.rlp.de
 | 
				
			||||||
 | 
					Created on: 30.11.20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					from django.urls import path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from compensation.views import *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					app_name = "compensation"
 | 
				
			||||||
 | 
					urlpatterns = [
 | 
				
			||||||
 | 
					    # Main compensation
 | 
				
			||||||
 | 
					    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('remove/<id>', remove_view, name='remove'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Eco-account
 | 
				
			||||||
 | 
					    path("account/", account_index_view, name="account-index"),
 | 
				
			||||||
 | 
					    path('account/new/', account_new_view, name='account-new'),
 | 
				
			||||||
 | 
					    path('account/open/<id>', account_open_view, name='account-open'),
 | 
				
			||||||
 | 
					    path('account/edit/<id>', account_edit_view, name='account-edit'),
 | 
				
			||||||
 | 
					    path('account/remove/<id>', account_remove_view, name='account-remove'),
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
							
								
								
									
										110
									
								
								compensation/views.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								compensation/views.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,110 @@
 | 
				
			|||||||
 | 
					from django.contrib.auth.decorators import login_required
 | 
				
			||||||
 | 
					from django.http import HttpRequest
 | 
				
			||||||
 | 
					from django.shortcuts import render
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from compensation.models import Compensation, EcoAccount
 | 
				
			||||||
 | 
					from compensation.tables import CompensationTable, EcoAccountTable
 | 
				
			||||||
 | 
					from konova.contexts import BaseContext
 | 
				
			||||||
 | 
					from konova.decorators import *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@login_required
 | 
				
			||||||
 | 
					@resolve_user_role
 | 
				
			||||||
 | 
					def index_view(request: HttpRequest):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Renders the index view for compensation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Args:
 | 
				
			||||||
 | 
					        request (HttpRequest): The incoming request
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Returns:
 | 
				
			||||||
 | 
					        A rendered view
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    template = "generic_index.html"
 | 
				
			||||||
 | 
					    user = request.user
 | 
				
			||||||
 | 
					    compensations = Compensation.get_role_objects(user)
 | 
				
			||||||
 | 
					    table = CompensationTable(
 | 
				
			||||||
 | 
					        request=request,
 | 
				
			||||||
 | 
					        queryset=compensations
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    context = {
 | 
				
			||||||
 | 
					        "table": table,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    context = BaseContext(request, context).context
 | 
				
			||||||
 | 
					    return render(request, template, context)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@login_required
 | 
				
			||||||
 | 
					def new_view(request: HttpRequest):
 | 
				
			||||||
 | 
					    # ToDo
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@login_required
 | 
				
			||||||
 | 
					def edit_view(request: HttpRequest, id: str):
 | 
				
			||||||
 | 
					    # ToDo
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@login_required
 | 
				
			||||||
 | 
					def open_view(request: HttpRequest, id: str):
 | 
				
			||||||
 | 
					    # ToDo
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@login_required
 | 
				
			||||||
 | 
					def remove_view(request: HttpRequest, id: str):
 | 
				
			||||||
 | 
					    # ToDo
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@login_required
 | 
				
			||||||
 | 
					def account_index_view(request: HttpRequest):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Renders the index view for eco accounts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Args:
 | 
				
			||||||
 | 
					        request (HttpRequest): The incoming request
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Returns:
 | 
				
			||||||
 | 
					        A rendered view
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    template = "generic_index.html"
 | 
				
			||||||
 | 
					    user = request.user
 | 
				
			||||||
 | 
					    eco_accounts = EcoAccount.objects.filter(
 | 
				
			||||||
 | 
					        created_by=user,
 | 
				
			||||||
 | 
					        is_deleted=False,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    table = EcoAccountTable(
 | 
				
			||||||
 | 
					        request=request,
 | 
				
			||||||
 | 
					        queryset=eco_accounts
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    context = {
 | 
				
			||||||
 | 
					        "table": table,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    context = BaseContext(request, context).context
 | 
				
			||||||
 | 
					    return render(request, template, context)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@login_required
 | 
				
			||||||
 | 
					def account_new_view(request: HttpRequest):
 | 
				
			||||||
 | 
					    # ToDo
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@login_required
 | 
				
			||||||
 | 
					def account_edit_view(request: HttpRequest, id: str):
 | 
				
			||||||
 | 
					    # ToDo
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@login_required
 | 
				
			||||||
 | 
					def account_open_view(request: HttpRequest, id: str):
 | 
				
			||||||
 | 
					    # ToDo
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@login_required
 | 
				
			||||||
 | 
					def account_remove_view(request: HttpRequest, id: str):
 | 
				
			||||||
 | 
					    # ToDo
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
							
								
								
									
										0
									
								
								intervention/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								intervention/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										18
									
								
								intervention/admin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								intervention/admin.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					from django.contrib import admin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from intervention.models import Intervention
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class InterventionAdmin(admin.ModelAdmin):
 | 
				
			||||||
 | 
					    list_display = [
 | 
				
			||||||
 | 
					        "id",
 | 
				
			||||||
 | 
					        "title",
 | 
				
			||||||
 | 
					        "type",
 | 
				
			||||||
 | 
					        "handler",
 | 
				
			||||||
 | 
					        "created_on",
 | 
				
			||||||
 | 
					        "is_active",
 | 
				
			||||||
 | 
					        "is_deleted",
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					admin.site.register(Intervention, InterventionAdmin)
 | 
				
			||||||
							
								
								
									
										5
									
								
								intervention/apps.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								intervention/apps.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					from django.apps import AppConfig
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class InterventionConfig(AppConfig):
 | 
				
			||||||
 | 
					    name = 'intervention'
 | 
				
			||||||
							
								
								
									
										220
									
								
								intervention/forms.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										220
									
								
								intervention/forms.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,220 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Author: Michel Peltriaux
 | 
				
			||||||
 | 
					Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
				
			||||||
 | 
					Contact: michel.peltriaux@sgdnord.rlp.de
 | 
				
			||||||
 | 
					Created on: 02.12.20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					from dal import autocomplete
 | 
				
			||||||
 | 
					from django import forms
 | 
				
			||||||
 | 
					from django.contrib.auth.models import User
 | 
				
			||||||
 | 
					from django.contrib.gis import forms as gis_forms
 | 
				
			||||||
 | 
					from django.contrib.gis.geos import Polygon
 | 
				
			||||||
 | 
					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 konova.models import Document
 | 
				
			||||||
 | 
					from konova.settings import DEFAULT_LAT, DEFAULT_LON, DEFAULT_ZOOM
 | 
				
			||||||
 | 
					from organisation.models import Organisation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class NewInterventionForm(BaseForm):
 | 
				
			||||||
 | 
					    identifier = forms.CharField(
 | 
				
			||||||
 | 
					        label=_("Identifier"),
 | 
				
			||||||
 | 
					        label_suffix="",
 | 
				
			||||||
 | 
					        max_length=255,
 | 
				
			||||||
 | 
					        help_text=_("Generated automatically if none was given"),
 | 
				
			||||||
 | 
					        required=False,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    title = forms.CharField(
 | 
				
			||||||
 | 
					        label=_("Title"),
 | 
				
			||||||
 | 
					        label_suffix="",
 | 
				
			||||||
 | 
					        max_length=255,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    type = forms.CharField(
 | 
				
			||||||
 | 
					        label=_("Type"),
 | 
				
			||||||
 | 
					        label_suffix="",
 | 
				
			||||||
 | 
					        max_length=255,
 | 
				
			||||||
 | 
					        help_text=_("Which intervention type is this"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    law = forms.CharField(
 | 
				
			||||||
 | 
					        label=_("Law"),
 | 
				
			||||||
 | 
					        label_suffix="",
 | 
				
			||||||
 | 
					        max_length=255,
 | 
				
			||||||
 | 
					        help_text=_("Based on which law"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    handler = forms.CharField(
 | 
				
			||||||
 | 
					        label=_("Intervention handler"),
 | 
				
			||||||
 | 
					        label_suffix="",
 | 
				
			||||||
 | 
					        max_length=255,
 | 
				
			||||||
 | 
					        help_text=_("Who performs the intervention"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    data_provider = forms.ModelChoiceField(
 | 
				
			||||||
 | 
					        label=_("Data provider"),
 | 
				
			||||||
 | 
					        label_suffix="",
 | 
				
			||||||
 | 
					        help_text=_("Who provides the data for the intervention"),
 | 
				
			||||||
 | 
					        queryset=Organisation.objects.all(),
 | 
				
			||||||
 | 
					        widget=autocomplete.ModelSelect2(
 | 
				
			||||||
 | 
					            url="other-orgs-autocomplete",
 | 
				
			||||||
 | 
					            attrs={
 | 
				
			||||||
 | 
					                "data-placeholder": _("Organization"),
 | 
				
			||||||
 | 
					                "data-minimum-input-length": 3,
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    data_provider_detail = forms.CharField(
 | 
				
			||||||
 | 
					        label=_("Data provider details"),
 | 
				
			||||||
 | 
					        label_suffix="",
 | 
				
			||||||
 | 
					        max_length=255,
 | 
				
			||||||
 | 
					        help_text=_("Further details"),
 | 
				
			||||||
 | 
					        required=False,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    geometry = gis_forms.MultiPolygonField(
 | 
				
			||||||
 | 
					        widget=gis_forms.OSMWidget(
 | 
				
			||||||
 | 
					            attrs={
 | 
				
			||||||
 | 
					                "default_lat": DEFAULT_LAT,
 | 
				
			||||||
 | 
					                "default_lon": DEFAULT_LON,
 | 
				
			||||||
 | 
					                "default_zoom": DEFAULT_ZOOM,
 | 
				
			||||||
 | 
					                'map_width': 800,
 | 
				
			||||||
 | 
					                'map_height': 500
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        label=_("Map"),
 | 
				
			||||||
 | 
					        label_suffix="",
 | 
				
			||||||
 | 
					        help_text=_("Where does the intervention take place")
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    documents = forms.FileField(
 | 
				
			||||||
 | 
					        widget=forms.ClearableFileInput(
 | 
				
			||||||
 | 
					            attrs={
 | 
				
			||||||
 | 
					                "multiple": True,
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        label=_("Files"),
 | 
				
			||||||
 | 
					        label_suffix="",
 | 
				
			||||||
 | 
					        required=False,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, *args, **kwargs):
 | 
				
			||||||
 | 
					        super().__init__(*args, **kwargs)
 | 
				
			||||||
 | 
					        self.form_title = _("New intervention")
 | 
				
			||||||
 | 
					        self.action_url = reverse("intervention:new")
 | 
				
			||||||
 | 
					        self.cancel_redirect = reverse("intervention:index")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def save(self, user: User):
 | 
				
			||||||
 | 
					        with transaction.atomic():
 | 
				
			||||||
 | 
					            identifier = self.cleaned_data.get("identifier", None)
 | 
				
			||||||
 | 
					            title = self.cleaned_data.get("title", None)
 | 
				
			||||||
 | 
					            _type = self.cleaned_data.get("type", None)
 | 
				
			||||||
 | 
					            law = self.cleaned_data.get("law", None)
 | 
				
			||||||
 | 
					            handler = self.cleaned_data.get("handler", None)
 | 
				
			||||||
 | 
					            data_provider = self.cleaned_data.get("data_provider", None)
 | 
				
			||||||
 | 
					            data_provider_detail = self.cleaned_data.get("data_provider_detail", None)
 | 
				
			||||||
 | 
					            geometry = self.cleaned_data.get("geometry", Polygon())
 | 
				
			||||||
 | 
					            documents = self.cleaned_data.get("documents", []) or []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            intervention = Intervention(
 | 
				
			||||||
 | 
					                identifier=identifier,
 | 
				
			||||||
 | 
					                title=title,
 | 
				
			||||||
 | 
					                type=_type,
 | 
				
			||||||
 | 
					                law=law,
 | 
				
			||||||
 | 
					                handler=handler,
 | 
				
			||||||
 | 
					                data_provider=data_provider,
 | 
				
			||||||
 | 
					                data_provider_detail=data_provider_detail,
 | 
				
			||||||
 | 
					                geometry=geometry,
 | 
				
			||||||
 | 
					                created_by=user,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            intervention.save()
 | 
				
			||||||
 | 
					            for doc in documents:
 | 
				
			||||||
 | 
					                doc_obj = Document()
 | 
				
			||||||
 | 
					                doc_obj.document = doc
 | 
				
			||||||
 | 
					                # ToDo Add functionality for other attributes
 | 
				
			||||||
 | 
					                doc_obj.save()
 | 
				
			||||||
 | 
					                intervention.documents.add(doc_obj)
 | 
				
			||||||
 | 
					        return intervention
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class EditInterventionForm(NewInterventionForm):
 | 
				
			||||||
 | 
					    def __init__(self, *args, **kwargs):
 | 
				
			||||||
 | 
					        super().__init__(*args, **kwargs)
 | 
				
			||||||
 | 
					        if self.instance is not None:
 | 
				
			||||||
 | 
					            self.action_url = reverse("intervention:edit", args=(self.instance.id,))
 | 
				
			||||||
 | 
					        self.cancel_redirect = reverse("intervention:index")
 | 
				
			||||||
 | 
					        self.form_title = _("Edit intervention")
 | 
				
			||||||
 | 
					        self.form_caption = ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Initialize form data
 | 
				
			||||||
 | 
					        form_data = {
 | 
				
			||||||
 | 
					            "identifier": self.instance.identifier,
 | 
				
			||||||
 | 
					            "title": self.instance.title,
 | 
				
			||||||
 | 
					            "type": self.instance.type,
 | 
				
			||||||
 | 
					            "law": self.instance.law,
 | 
				
			||||||
 | 
					            "handler": self.instance.handler,
 | 
				
			||||||
 | 
					            "data_provider": self.instance.data_provider,
 | 
				
			||||||
 | 
					            "data_provider_detail": self.instance.data_provider_detail,
 | 
				
			||||||
 | 
					            "geometry": self.instance.geometry,
 | 
				
			||||||
 | 
					            "documents": self.instance.documents.all(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        disabled_fields = [
 | 
				
			||||||
 | 
					            "identifier",
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        self.load_initial_data(
 | 
				
			||||||
 | 
					            form_data,
 | 
				
			||||||
 | 
					            disabled_fields,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def save(self, user: User):
 | 
				
			||||||
 | 
					        """ Overwrite instance with new form data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            user ():
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        with transaction.atomic():
 | 
				
			||||||
 | 
					            identifier = self.cleaned_data.get("identifier", None)
 | 
				
			||||||
 | 
					            title = self.cleaned_data.get("title", None)
 | 
				
			||||||
 | 
					            _type = self.cleaned_data.get("type", None)
 | 
				
			||||||
 | 
					            law = self.cleaned_data.get("law", None)
 | 
				
			||||||
 | 
					            handler = self.cleaned_data.get("handler", None)
 | 
				
			||||||
 | 
					            data_provider = self.cleaned_data.get("data_provider", None)
 | 
				
			||||||
 | 
					            data_provider_detail = self.cleaned_data.get("data_provider_detail", None)
 | 
				
			||||||
 | 
					            geometry = self.cleaned_data.get("geometry", Polygon())
 | 
				
			||||||
 | 
					            documents = self.cleaned_data.get("documents", []) or []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            self.instance.identifier = identifier
 | 
				
			||||||
 | 
					            self.instance.title = title
 | 
				
			||||||
 | 
					            self.instance.type = _type
 | 
				
			||||||
 | 
					            self.instance.law = law
 | 
				
			||||||
 | 
					            self.instance.handler = handler
 | 
				
			||||||
 | 
					            self.instance.data_provider = data_provider
 | 
				
			||||||
 | 
					            self.instance.data_provider_detail = data_provider_detail
 | 
				
			||||||
 | 
					            self.instance.geometry = geometry
 | 
				
			||||||
 | 
					            self.instance.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for doc in documents:
 | 
				
			||||||
 | 
					                doc_obj = Document()
 | 
				
			||||||
 | 
					                doc_obj.document = doc
 | 
				
			||||||
 | 
					                # ToDo Add functionality for other attributes
 | 
				
			||||||
 | 
					                doc_obj.save()
 | 
				
			||||||
 | 
					                self.instance.documents.add(doc_obj)
 | 
				
			||||||
 | 
					        return self.instance
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class OpenInterventionForm(EditInterventionForm):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    This form is not intended to be used as data-input form. It's used to simplify the rendering of intervention:open
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    def __init__(self, *args, **kwargs):
 | 
				
			||||||
 | 
					        super().__init__(*args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Resize map
 | 
				
			||||||
 | 
					        self.fields["geometry"].widget.attrs["map_width"] = 500
 | 
				
			||||||
 | 
					        self.fields["geometry"].widget.attrs["map_height"] = 300
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Disable all form fields
 | 
				
			||||||
 | 
					        for field in self.fields:
 | 
				
			||||||
 | 
					            self.disable_form_field(field)
 | 
				
			||||||
							
								
								
									
										91
									
								
								intervention/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								intervention/models.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,91 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					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.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.utils.generators import generate_random_string
 | 
				
			||||||
 | 
					from organisation.enums import RoleTypeEnum
 | 
				
			||||||
 | 
					from process.models import Process
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Intervention(BaseObject):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Interventions are e.g. construction sites where nature used to be.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    A process consists of exactly one intervention and one or more compensation
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    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")
 | 
				
			||||||
 | 
					    documents = models.ManyToManyField("konova.Document", blank=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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
 | 
				
			||||||
 | 
					    def __generate_new_identifier() -> str:
 | 
				
			||||||
 | 
					        """ Generates a new identifier for the intervention object
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					            str
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        curr_month = str(now().month)
 | 
				
			||||||
 | 
					        curr_year = str(now().year)
 | 
				
			||||||
 | 
					        rand_str = generate_random_string(
 | 
				
			||||||
 | 
					            length=INTERVENTION_IDENTIFIER_LENGTH,
 | 
				
			||||||
 | 
					            only_numbers=True,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        _str = "{}{}{}".format(curr_month, curr_year, rand_str)
 | 
				
			||||||
 | 
					        return INTERVENTION_IDENTIFIER_TEMPLATE.format(_str)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def save(self, *args, **kwargs):
 | 
				
			||||||
 | 
					        if self.identifier is None or len(self.identifier) == 0:
 | 
				
			||||||
 | 
					            # Create new identifier
 | 
				
			||||||
 | 
					            new_id = self.__generate_new_identifier()
 | 
				
			||||||
 | 
					            while Intervention.objects.filter(identifier=new_id).exists():
 | 
				
			||||||
 | 
					                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
 | 
				
			||||||
							
								
								
									
										9
									
								
								intervention/settings.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								intervention/settings.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Author: Michel Peltriaux
 | 
				
			||||||
 | 
					Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
				
			||||||
 | 
					Contact: michel.peltriaux@sgdnord.rlp.de
 | 
				
			||||||
 | 
					Created on: 30.11.20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					INTERVENTION_IDENTIFIER_LENGTH = 10
 | 
				
			||||||
 | 
					INTERVENTION_IDENTIFIER_TEMPLATE = "EIV-{}"
 | 
				
			||||||
							
								
								
									
										87
									
								
								intervention/tables.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								intervention/tables.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,87 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Author: Michel Peltriaux
 | 
				
			||||||
 | 
					Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
				
			||||||
 | 
					Contact: michel.peltriaux@sgdnord.rlp.de
 | 
				
			||||||
 | 
					Created on: 01.12.20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					from django.urls import reverse
 | 
				
			||||||
 | 
					from django.utils.html import format_html
 | 
				
			||||||
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from intervention.models import Intervention
 | 
				
			||||||
 | 
					from konova.utils.tables import BaseTable
 | 
				
			||||||
 | 
					import django_tables2 as tables
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class InterventionTable(BaseTable):
 | 
				
			||||||
 | 
					    id = tables.Column(
 | 
				
			||||||
 | 
					        verbose_name=_("Identifier"),
 | 
				
			||||||
 | 
					        orderable=True,
 | 
				
			||||||
 | 
					        accessor="identifier",
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    t = tables.Column(
 | 
				
			||||||
 | 
					        verbose_name=_("Title"),
 | 
				
			||||||
 | 
					        orderable=True,
 | 
				
			||||||
 | 
					        accessor="title",
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    p = tables.Column(
 | 
				
			||||||
 | 
					        verbose_name=_("Process"),
 | 
				
			||||||
 | 
					        orderable=True,
 | 
				
			||||||
 | 
					        accessor="process",
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    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 = _("Interventions")
 | 
				
			||||||
 | 
					        self.add_new_url = reverse("intervention:new")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def render_id(self, value, record: Intervention):
 | 
				
			||||||
 | 
					        """ Renders the id column for an intervention
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            value (str): The identifier value
 | 
				
			||||||
 | 
					            record (Intervention): The intervention record
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        html = ""
 | 
				
			||||||
 | 
					        html += self.render_link(
 | 
				
			||||||
 | 
					            tooltip=_("Open {}").format(_("Intervention")),
 | 
				
			||||||
 | 
					            href=reverse("intervention:open", args=(record.id,)),
 | 
				
			||||||
 | 
					            txt=value,
 | 
				
			||||||
 | 
					            new_tab=False,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        return format_html(html)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def render_ac(self, value, record):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Renders possible actions for this record, such as delete.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        intervention = _("Intervention")
 | 
				
			||||||
 | 
					        html = ""
 | 
				
			||||||
 | 
					        html += self.render_open_btn(
 | 
				
			||||||
 | 
					            _("Open {}").format(intervention),
 | 
				
			||||||
 | 
					            reverse("intervention:open", args=(record.id,))
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        html += self.render_edit_btn(
 | 
				
			||||||
 | 
					            _("Edit {}").format(intervention),
 | 
				
			||||||
 | 
					            reverse("intervention:edit", args=(record.id,)),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        html += self.render_delete_btn(
 | 
				
			||||||
 | 
					            _("Delete {}").format(intervention),
 | 
				
			||||||
 | 
					            reverse("intervention:remove", args=(record.id,)),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        return format_html(html)
 | 
				
			||||||
							
								
								
									
										47
									
								
								intervention/templates/intervention/open.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								intervention/templates/intervention/open.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					{% extends 'base.html' %}
 | 
				
			||||||
 | 
					{% load i18n static %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block body %}
 | 
				
			||||||
 | 
					    <div class="rows">
 | 
				
			||||||
 | 
					        <div class="columns">
 | 
				
			||||||
 | 
					            <div class="large-10 column">
 | 
				
			||||||
 | 
					                <h3>{% trans 'Intervention' %}: {{ intervention.title }}</h3>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="large-2 column">
 | 
				
			||||||
 | 
					                <a href="{% url 'intervention:edit' intervention.id %}">
 | 
				
			||||||
 | 
					                    <button class="button small" role="button" value="{% trans 'Edit' %}"><i class='fas fa-edit'></i> {% trans 'Edit' %}</button>
 | 
				
			||||||
 | 
					                </a>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <hr>
 | 
				
			||||||
 | 
					        {% comment %}
 | 
				
			||||||
 | 
					        form.media needs to be loaded to ensure the openlayers client will be loaded properly
 | 
				
			||||||
 | 
					        {% endcomment %}
 | 
				
			||||||
 | 
					        {{ form.media }}
 | 
				
			||||||
 | 
					        <div class="columns">
 | 
				
			||||||
 | 
					            <div class="small-6 columns">
 | 
				
			||||||
 | 
					                <table>
 | 
				
			||||||
 | 
					                    <tbody>
 | 
				
			||||||
 | 
					                        {% for field in form %}
 | 
				
			||||||
 | 
					                            {% if field != form.geometry %}
 | 
				
			||||||
 | 
					                            <tr title="{{ field.help_text }}" class="{% if field.errors %}error{% endif %}">
 | 
				
			||||||
 | 
					                                <th scope="row" class="small-3">
 | 
				
			||||||
 | 
					                                    <div>{{ field.label }}</div>
 | 
				
			||||||
 | 
					                                    <small>{{ field.help_text }}</small>
 | 
				
			||||||
 | 
					                                </th>
 | 
				
			||||||
 | 
					                                <td class="small-12">
 | 
				
			||||||
 | 
					                                    {{ field }}
 | 
				
			||||||
 | 
					                                </td>
 | 
				
			||||||
 | 
					                            </tr>
 | 
				
			||||||
 | 
					                            {% endif %}
 | 
				
			||||||
 | 
					                        {% endfor %}
 | 
				
			||||||
 | 
					                    </tbody>
 | 
				
			||||||
 | 
					                </table>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="small-6 columns">
 | 
				
			||||||
 | 
					                {{ form.geometry }}
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
							
								
								
									
										3
									
								
								intervention/tests.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								intervention/tests.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					from django.test import TestCase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Create your tests here.
 | 
				
			||||||
							
								
								
									
										19
									
								
								intervention/urls.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								intervention/urls.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Author: Michel Peltriaux
 | 
				
			||||||
 | 
					Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
				
			||||||
 | 
					Contact: michel.peltriaux@sgdnord.rlp.de
 | 
				
			||||||
 | 
					Created on: 30.11.20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					from django.urls import path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from intervention.views import index_view, new_view, open_view, edit_view, remove_view
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					app_name = "intervention"
 | 
				
			||||||
 | 
					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('remove/<id>', remove_view, name='remove'),
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
							
								
								
									
										171
									
								
								intervention/views.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								intervention/views.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,171 @@
 | 
				
			|||||||
 | 
					from django.contrib import messages
 | 
				
			||||||
 | 
					from django.contrib.auth.decorators import login_required
 | 
				
			||||||
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
 | 
					from django.http import HttpRequest
 | 
				
			||||||
 | 
					from django.shortcuts import render, get_object_or_404, redirect
 | 
				
			||||||
 | 
					from django.urls import reverse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from intervention.forms import NewInterventionForm, EditInterventionForm, OpenInterventionForm
 | 
				
			||||||
 | 
					from intervention.models import Intervention
 | 
				
			||||||
 | 
					from intervention.tables import InterventionTable
 | 
				
			||||||
 | 
					from konova.contexts import BaseContext
 | 
				
			||||||
 | 
					from konova.decorators import *
 | 
				
			||||||
 | 
					from konova.forms import RemoveForm
 | 
				
			||||||
 | 
					from process.models import Process
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@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
 | 
				
			||||||
 | 
					    interventions = Intervention.get_role_objects(user)
 | 
				
			||||||
 | 
					    table = InterventionTable(
 | 
				
			||||||
 | 
					        request=request,
 | 
				
			||||||
 | 
					        queryset=interventions
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    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 intervention creation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Args:
 | 
				
			||||||
 | 
					        request (HttpRequest): The incoming request
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Returns:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    template = "konova/form.html"
 | 
				
			||||||
 | 
					    form = NewInterventionForm(request.POST or None)
 | 
				
			||||||
 | 
					    if request.method == "POST":
 | 
				
			||||||
 | 
					        if form.is_valid():
 | 
				
			||||||
 | 
					            intervention = form.save(request.user)
 | 
				
			||||||
 | 
					            if intervention.process is None:
 | 
				
			||||||
 | 
					                # An intervention can not be created without a process -> automatically create a new process
 | 
				
			||||||
 | 
					                process = Process.create_from_intervention(intervention)
 | 
				
			||||||
 | 
					                messages.info(request, _("Interventions must be part of a process. Please fill in the missing data for the process"))
 | 
				
			||||||
 | 
					                return redirect("process:edit", id=process.id)
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                messages.success(request, _("Intervention {} added").format(intervention.title))
 | 
				
			||||||
 | 
					                return redirect("intervention:index")
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            messages.error(request, _("Invalid input"))
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        # For clarification: nothing in this case
 | 
				
			||||||
 | 
					        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 view for viewing an intervention's data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Args:
 | 
				
			||||||
 | 
					        request (HttpRequest): The incoming request
 | 
				
			||||||
 | 
					        id (str): The intervention's id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Returns:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    template = "intervention/open.html"
 | 
				
			||||||
 | 
					    intervention = get_object_or_404(Intervention, id=id)
 | 
				
			||||||
 | 
					    form = OpenInterventionForm(instance=intervention)
 | 
				
			||||||
 | 
					    context = {
 | 
				
			||||||
 | 
					        "intervention": intervention,
 | 
				
			||||||
 | 
					        "form": form,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    context = BaseContext(request, context).context
 | 
				
			||||||
 | 
					    return render(request, template, context)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@login_required
 | 
				
			||||||
 | 
					def edit_view(request: HttpRequest, id: str):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Renders a view for editing interventions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Args:
 | 
				
			||||||
 | 
					        request (HttpRequest): The incoming request
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Returns:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    template = "konova/form.html"
 | 
				
			||||||
 | 
					    intervention = get_object_or_404(Intervention, id=id)
 | 
				
			||||||
 | 
					    if request.method == "POST":
 | 
				
			||||||
 | 
					        form = EditInterventionForm(request.POST or None, instance=intervention)
 | 
				
			||||||
 | 
					        if form.is_valid():
 | 
				
			||||||
 | 
					            intervention = form.save(request.user)
 | 
				
			||||||
 | 
					            messages.success(request, _("{} edited").format(intervention))
 | 
				
			||||||
 | 
					            return redirect("intervention:index")
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            messages.error(request, _("Invalid input"))
 | 
				
			||||||
 | 
					    form = EditInterventionForm(instance=intervention)
 | 
				
			||||||
 | 
					    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"
 | 
				
			||||||
 | 
					    # Since an intervention is always organized inside a process, we will call the process removing routine, which
 | 
				
			||||||
 | 
					    # disables all related elements by default
 | 
				
			||||||
 | 
					    obj = get_object_or_404(Intervention, id=id)
 | 
				
			||||||
 | 
					    process = obj.process
 | 
				
			||||||
 | 
					    if request.method == "POST":
 | 
				
			||||||
 | 
					        form = RemoveForm(
 | 
				
			||||||
 | 
					            request.POST or None,
 | 
				
			||||||
 | 
					            object_to_remove=obj,
 | 
				
			||||||
 | 
					            remove_post_url=reverse("process:remove", args=(process.id,)),
 | 
				
			||||||
 | 
					            cancel_url=reverse("intervention:index"),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        if form.is_valid():
 | 
				
			||||||
 | 
					            confirmed = form.is_checked()
 | 
				
			||||||
 | 
					            if confirmed:
 | 
				
			||||||
 | 
					                process.deactivate()
 | 
				
			||||||
 | 
					                messages.success(request, _("Intervention {} removed").format(obj))
 | 
				
			||||||
 | 
					            return redirect("intervention:index")
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            messages.error(request, _("Invalid input"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    form = RemoveForm(
 | 
				
			||||||
 | 
					        object_to_remove=obj,
 | 
				
			||||||
 | 
					        remove_post_url=reverse("process:remove", args=(process.id,)),
 | 
				
			||||||
 | 
					        cancel_url=reverse("intervention:index"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    context = {
 | 
				
			||||||
 | 
					        "form": form,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    context = BaseContext(request, context).context
 | 
				
			||||||
 | 
					    return render(request, template, context)
 | 
				
			||||||
							
								
								
									
										0
									
								
								kspneo/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								kspneo/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										32
									
								
								kspneo/admin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								kspneo/admin.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Author: Michel Peltriaux
 | 
				
			||||||
 | 
					Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
				
			||||||
 | 
					Contact: michel.peltriaux@sgdnord.rlp.de
 | 
				
			||||||
 | 
					Created on: 07.12.20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					from django.contrib import admin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from konova.models import RoleType, RoleGroup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class RoleTypeAdmin(admin.ModelAdmin):
 | 
				
			||||||
 | 
					    list_display = [
 | 
				
			||||||
 | 
					        "type",
 | 
				
			||||||
 | 
					        "created_on",
 | 
				
			||||||
 | 
					        "created_by",
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class RoleGroupAdmin(admin.ModelAdmin):
 | 
				
			||||||
 | 
					    list_display = [
 | 
				
			||||||
 | 
					        "name",
 | 
				
			||||||
 | 
					        "organisation",
 | 
				
			||||||
 | 
					        "role",
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					admin.site.register(RoleType, RoleTypeAdmin)
 | 
				
			||||||
 | 
					admin.site.register(RoleGroup, RoleGroupAdmin)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										16
									
								
								kspneo/asgi.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								kspneo/asgi.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					ASGI config for konova project.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					It exposes the ASGI callable as a module-level variable named ``application``.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For more information on this file, see
 | 
				
			||||||
 | 
					https://docs.djangoproject.com/en/3.1/howto/deployment/asgi/
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.core.asgi import get_asgi_application
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'konova.settings')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					application = get_asgi_application()
 | 
				
			||||||
							
								
								
									
										42
									
								
								kspneo/autocompletes.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								kspneo/autocompletes.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Author: Michel Peltriaux
 | 
				
			||||||
 | 
					Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
				
			||||||
 | 
					Contact: michel.peltriaux@sgdnord.rlp.de
 | 
				
			||||||
 | 
					Created on: 07.12.20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					from dal_select2.views import Select2QuerySetView
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from organisation.enums import OrganisationTypeEnum
 | 
				
			||||||
 | 
					from organisation.models import Organisation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class OrganisationAutocomplete(Select2QuerySetView):
 | 
				
			||||||
 | 
					    def get_queryset(self):
 | 
				
			||||||
 | 
					        if self.request.user.is_anonymous:
 | 
				
			||||||
 | 
					            return Organisation.objects.none()
 | 
				
			||||||
 | 
					        qs = Organisation.objects.all()
 | 
				
			||||||
 | 
					        if self.q:
 | 
				
			||||||
 | 
					            qs = qs.filter(name__icontains=self.q)
 | 
				
			||||||
 | 
					            qs = qs.order_by(
 | 
				
			||||||
 | 
					                "name"
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        return qs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class NonOfficialOrganisationAutocomplete(Select2QuerySetView):
 | 
				
			||||||
 | 
					    def get_queryset(self):
 | 
				
			||||||
 | 
					        if self.request.user.is_anonymous:
 | 
				
			||||||
 | 
					            return Organisation.objects.none()
 | 
				
			||||||
 | 
					        qs = Organisation.objects.all()
 | 
				
			||||||
 | 
					        if self.q:
 | 
				
			||||||
 | 
					            qs = qs.filter(
 | 
				
			||||||
 | 
					                name__icontains=self.q,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            qs = qs.exclude(
 | 
				
			||||||
 | 
					                type=OrganisationTypeEnum.OFFICIAL.value
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            qs = qs.order_by(
 | 
				
			||||||
 | 
					                "name"
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        return qs
 | 
				
			||||||
							
								
								
									
										53
									
								
								kspneo/contexts.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								kspneo/contexts.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Author: Michel Peltriaux
 | 
				
			||||||
 | 
					Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
				
			||||||
 | 
					Contact: michel.peltriaux@sgdnord.rlp.de
 | 
				
			||||||
 | 
					Created on: 16.11.20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					from django.http import HttpRequest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from konova.models import RoleGroup
 | 
				
			||||||
 | 
					from konova.sub_settings.context_settings import BASE_TITLE, WIKI_URL, BASE_FRONTEND_TITLE, RENDER_HEADER
 | 
				
			||||||
 | 
					from konova.utils.session import set_session_user_role
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BaseContext:
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Holds all base data which is needed for every context rendering
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    context = {
 | 
				
			||||||
 | 
					        "base_title": BASE_TITLE,
 | 
				
			||||||
 | 
					        "base_frontend_title": BASE_FRONTEND_TITLE,
 | 
				
			||||||
 | 
					        "language": "en",
 | 
				
			||||||
 | 
					        "wiki_url": WIKI_URL,
 | 
				
			||||||
 | 
					        "user": None,
 | 
				
			||||||
 | 
					        "render_header": RENDER_HEADER,
 | 
				
			||||||
 | 
					        "current_role": None,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, request: HttpRequest, additional_context: dict = {}):
 | 
				
			||||||
 | 
					        self.context["language"] = request.LANGUAGE_CODE
 | 
				
			||||||
 | 
					        self.context["user"] = request.user
 | 
				
			||||||
 | 
					        self.__handle_current_role(request)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Add additional context, derived from given parameters
 | 
				
			||||||
 | 
					        self.context.update(additional_context)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __handle_current_role(self, request: HttpRequest):
 | 
				
			||||||
 | 
					        """ Reads/Writes current role from/to session
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            request (HttpRequest): The incoming request
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        # Store current role in session object to reduce amount of db access
 | 
				
			||||||
 | 
					        current_role = request.session.get("current_role", {})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if len(current_role) == 0 and request.user.is_authenticated:
 | 
				
			||||||
 | 
					            role_group = RoleGroup.get_users_role_groups(request.user).first()
 | 
				
			||||||
 | 
					            current_role = set_session_user_role(request, role_group)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.context["current_role"] = current_role
 | 
				
			||||||
							
								
								
									
										94
									
								
								kspneo/decorators.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								kspneo/decorators.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,94 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Author: Michel Peltriaux
 | 
				
			||||||
 | 
					Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
				
			||||||
 | 
					Contact: michel.peltriaux@sgdnord.rlp.de
 | 
				
			||||||
 | 
					Created on: 16.11.20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from functools import wraps
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.contrib import messages
 | 
				
			||||||
 | 
					from django.core.exceptions import ObjectDoesNotExist
 | 
				
			||||||
 | 
					from django.shortcuts import redirect
 | 
				
			||||||
 | 
					from django.urls import reverse
 | 
				
			||||||
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from konova.models import RoleGroup
 | 
				
			||||||
 | 
					from konova.utils.session import get_session_user_role
 | 
				
			||||||
 | 
					from organisation.enums import RoleTypeEnum
 | 
				
			||||||
 | 
					from process.enums import PROCESS_EDITABLE_STATE
 | 
				
			||||||
 | 
					from process.models import Process
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def staff_required(function):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    A decorator for functions which shall only be usable for staff members of the system
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    @wraps(function)
 | 
				
			||||||
 | 
					    def wrap(request, *args, **kwargs):
 | 
				
			||||||
 | 
					        user = request.user
 | 
				
			||||||
 | 
					        if user.is_staff:
 | 
				
			||||||
 | 
					            return function(request, *args, **kwargs)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            messages.info(request, _("You need to be staff to perform this action!"))
 | 
				
			||||||
 | 
					            return redirect(request.META.get("HTTP_REFERER", reverse("home")))
 | 
				
			||||||
 | 
					    return wrap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def superuser_required(function):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    A decorator for functions which shall only be usable for superusers of the system
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    @wraps(function)
 | 
				
			||||||
 | 
					    def wrap(request, *args, **kwargs):
 | 
				
			||||||
 | 
					        user = request.user
 | 
				
			||||||
 | 
					        if user.is_superuser:
 | 
				
			||||||
 | 
					            return function(request, *args, **kwargs)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            messages.info(request, _("You need to be administrator to perform this action!"))
 | 
				
			||||||
 | 
					            return redirect(request.META.get("HTTP_REFERER", reverse("home")))
 | 
				
			||||||
 | 
					    return wrap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def resolve_user_role(function):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    A decorator for functions to resolve the current user role and store it in the user object
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    @wraps(function)
 | 
				
			||||||
 | 
					    def wrap(request, *args, **kwargs):
 | 
				
			||||||
 | 
					        user = request.user
 | 
				
			||||||
 | 
					        role = get_session_user_role(request)
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            role = RoleGroup.objects.get(id=role.get("id", -1))
 | 
				
			||||||
 | 
					            user.current_role = role
 | 
				
			||||||
 | 
					        except ObjectDoesNotExist:
 | 
				
			||||||
 | 
					            user.current_role = None
 | 
				
			||||||
 | 
					        return function(request, *args, **kwargs)
 | 
				
			||||||
 | 
					    return wrap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def valid_process_role_required(function):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    A decorator for functions to check whether the user has a valid role selected
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    @wraps(function)
 | 
				
			||||||
 | 
					    def wrap(request, *args, **kwargs):
 | 
				
			||||||
 | 
					        user = request.user
 | 
				
			||||||
 | 
					        if user.current_role is None:
 | 
				
			||||||
 | 
					            role = get_session_user_role(request)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            role = user.current_role
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            process = Process.objects.get(id=kwargs.get("id"))
 | 
				
			||||||
 | 
					            editable = PROCESS_EDITABLE_STATE.get(process.state)
 | 
				
			||||||
 | 
					            role_enum = RoleTypeEnum[role.role.type]
 | 
				
			||||||
 | 
					            if role_enum in editable:
 | 
				
			||||||
 | 
					                return function(request, *args, **kwargs)
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                messages.error(request, _("Your current role is not allowed to do this"))
 | 
				
			||||||
 | 
					                return redirect(request.META.get("HTTP_REFERER", "home"))
 | 
				
			||||||
 | 
					        except ObjectDoesNotExist:
 | 
				
			||||||
 | 
					            process = None
 | 
				
			||||||
 | 
					        return function(request, *args, **kwargs)
 | 
				
			||||||
 | 
					    return wrap
 | 
				
			||||||
							
								
								
									
										40
									
								
								kspneo/enums.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								kspneo/enums.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Author: Michel Peltriaux
 | 
				
			||||||
 | 
					Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
				
			||||||
 | 
					Contact: michel.peltriaux@sgdnord.rlp.de
 | 
				
			||||||
 | 
					Created on: 17.11.20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from enum import Enum
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BaseEnum(Enum):
 | 
				
			||||||
 | 
					    """ Provides basic functionality for Enums
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def as_choices(cls, drop_empty_choice: bool = False):
 | 
				
			||||||
 | 
					        empty_choice = [] if drop_empty_choice else [(None, "---")]
 | 
				
			||||||
 | 
					        choices = empty_choice + [(enum.value, enum.name) for enum in cls]
 | 
				
			||||||
 | 
					        return choices
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class UnitEnum(BaseEnum):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Predefines units for selection
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    mm = "mm"
 | 
				
			||||||
 | 
					    dm = "dm"
 | 
				
			||||||
 | 
					    cm = "cm"
 | 
				
			||||||
 | 
					    m = "m"
 | 
				
			||||||
 | 
					    km = "km"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    qmm = "qmm"
 | 
				
			||||||
 | 
					    qdm = "qdm"
 | 
				
			||||||
 | 
					    qcm = "qcm"
 | 
				
			||||||
 | 
					    qm = "qm"
 | 
				
			||||||
 | 
					    qkm = "qkm"
 | 
				
			||||||
 | 
					    ha = "ha"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    st = "St."  # pieces
 | 
				
			||||||
							
								
								
									
										141
									
								
								kspneo/forms.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								kspneo/forms.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,141 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Author: Michel Peltriaux
 | 
				
			||||||
 | 
					Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
				
			||||||
 | 
					Contact: michel.peltriaux@sgdnord.rlp.de
 | 
				
			||||||
 | 
					Created on: 16.11.20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from abc import abstractmethod
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django import forms
 | 
				
			||||||
 | 
					from django.http import HttpRequest
 | 
				
			||||||
 | 
					from django.urls import reverse
 | 
				
			||||||
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from konova.models import RoleGroup
 | 
				
			||||||
 | 
					from konova.utils.session import set_session_user_role
 | 
				
			||||||
 | 
					from organisation.settings import ROLE_TYPE_STRINGS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BaseForm(forms.Form):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Basic form for that holds attributes needed in all other forms
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    action_url = None
 | 
				
			||||||
 | 
					    form_title = None
 | 
				
			||||||
 | 
					    cancel_redirect = None
 | 
				
			||||||
 | 
					    form_caption = None
 | 
				
			||||||
 | 
					    instance = None  # The data holding model object
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, *args, **kwargs):
 | 
				
			||||||
 | 
					        self.instance = kwargs.pop("instance", None)
 | 
				
			||||||
 | 
					        super().__init__(*args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @abstractmethod
 | 
				
			||||||
 | 
					    def save(self):
 | 
				
			||||||
 | 
					        # To be implemented in subclasses!
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def disable_form_field(self, field: str):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Disables a form field for user editing
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.fields[field].widget.attrs["readonly"] = True
 | 
				
			||||||
 | 
					        self.fields[field].disabled = True
 | 
				
			||||||
 | 
					        self.fields[field].widget.attrs["title"] = _("Not editable")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def initialize_form_field(self, field: str, val):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Initializes a form field with a value
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.fields[field].initial = val
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def load_initial_data(self, form_data: dict, disabled_fields: list):
 | 
				
			||||||
 | 
					        """ Initializes form data from instance
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Inserts instance data into form and disables form fields
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        if self.instance is None:
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        for k, v in form_data.items():
 | 
				
			||||||
 | 
					            self.initialize_form_field(k, v)
 | 
				
			||||||
 | 
					        for field in disabled_fields:
 | 
				
			||||||
 | 
					            self.disable_form_field(field)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class RemoveForm(BaseForm):
 | 
				
			||||||
 | 
					    check = forms.BooleanField(
 | 
				
			||||||
 | 
					        label=_("Confirm"),
 | 
				
			||||||
 | 
					        label_suffix=_(""),
 | 
				
			||||||
 | 
					        required=True,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, *args, **kwargs):
 | 
				
			||||||
 | 
					        self.object_to_remove = kwargs.pop("object_to_remove", None)
 | 
				
			||||||
 | 
					        self.remove_post_url = kwargs.pop("remove_post_url", "")
 | 
				
			||||||
 | 
					        self.cancel_url = kwargs.pop("cancel_url", "")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        super().__init__(*args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.form_title = _("Remove")
 | 
				
			||||||
 | 
					        if self.object_to_remove is not None:
 | 
				
			||||||
 | 
					            self.form_caption = _("You are about to remove {} {}").format(self.object_to_remove.__class__.__name__, self.object_to_remove)
 | 
				
			||||||
 | 
					        self.action_url = self.remove_post_url
 | 
				
			||||||
 | 
					        self.cancel_redirect = self.cancel_url
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def is_checked(self) -> bool:
 | 
				
			||||||
 | 
					        return self.cleaned_data.get("check", False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def save(self):
 | 
				
			||||||
 | 
					        if self.object_to_remove is not None and self.is_checked():
 | 
				
			||||||
 | 
					            self.object_to_remove.is_active = False
 | 
				
			||||||
 | 
					            self.object_to_remove.is_deleted = True
 | 
				
			||||||
 | 
					            self.object_to_remove.save()
 | 
				
			||||||
 | 
					        return self.object_to_remove
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ChangeUserRoleForm(BaseForm):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Form for a user to change the current role
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    role = forms.ChoiceField(
 | 
				
			||||||
 | 
					        label=_("You are working as"),
 | 
				
			||||||
 | 
					        label_suffix="",
 | 
				
			||||||
 | 
					        choices=[],
 | 
				
			||||||
 | 
					        widget=forms.Select(
 | 
				
			||||||
 | 
					            attrs={
 | 
				
			||||||
 | 
					                "onchange": "submit();",
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, *args, **kwargs):
 | 
				
			||||||
 | 
					        user = kwargs.pop("user", None)
 | 
				
			||||||
 | 
					        super().__init__(*args, **kwargs)
 | 
				
			||||||
 | 
					        self.action_url = reverse("home")
 | 
				
			||||||
 | 
					        self.cancel_redirect = reverse("home")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        role_groups = RoleGroup.get_users_role_groups(user)
 | 
				
			||||||
 | 
					        choices = []
 | 
				
			||||||
 | 
					        for group in role_groups:
 | 
				
			||||||
 | 
					            choices.append(
 | 
				
			||||||
 | 
					                (group.id, "{} ({})".format(ROLE_TYPE_STRINGS.get(group.role.type, None), group.organisation))
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        self.fields["role"].choices = choices
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def save(self, request: HttpRequest) -> RoleGroup:
 | 
				
			||||||
 | 
					        """ Custom save method for storing the newly selected role
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            request (HttpRequest):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        role_group = RoleGroup.get_users_role_groups(request.user).get(id=self.cleaned_data.get("role", -1))
 | 
				
			||||||
 | 
					        set_session_user_role(request, role_group)
 | 
				
			||||||
 | 
					        return role_group
 | 
				
			||||||
							
								
								
									
										153
									
								
								kspneo/management/commands/setup.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								kspneo/management/commands/setup.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,153 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Author: Michel Peltriaux
 | 
				
			||||||
 | 
					Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
				
			||||||
 | 
					Contact: michel.peltriaux@sgdnord.rlp.de
 | 
				
			||||||
 | 
					Created on: 15.12.20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					from getpass import getpass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.contrib.auth.models import User
 | 
				
			||||||
 | 
					from django.core.management import BaseCommand
 | 
				
			||||||
 | 
					from django.db import transaction
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from konova.management.commands.setup_test_data import TEST_ORGANISATION_DATA, TEST_ROLE_GROUPS_DATA
 | 
				
			||||||
 | 
					from konova.models import RoleType, RoleGroup
 | 
				
			||||||
 | 
					from organisation.enums import RoleTypeEnum
 | 
				
			||||||
 | 
					from organisation.models import Organisation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CREATED_TEMPLATE = "{} created"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Command(BaseCommand):
 | 
				
			||||||
 | 
					    help = "Initializes database with basic data"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def handle(self, *args, **options):
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            with transaction.atomic():
 | 
				
			||||||
 | 
					                self.__init_superuser()
 | 
				
			||||||
 | 
					                self.__init_test_organisation()
 | 
				
			||||||
 | 
					                self.__init_role_types()
 | 
				
			||||||
 | 
					                self.__init_role_groups()
 | 
				
			||||||
 | 
					        except KeyboardInterrupt:
 | 
				
			||||||
 | 
					            self.__break_line()
 | 
				
			||||||
 | 
					            exit(-1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init_superuser(self):
 | 
				
			||||||
 | 
					        """ Create a superuser by user prompt input
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.stdout.write(
 | 
				
			||||||
 | 
					            self.style.WARNING(
 | 
				
			||||||
 | 
					                "--- Superuser ---",
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        username = input("Superuser name: ")
 | 
				
			||||||
 | 
					        if User.objects.filter(username=username).exists():
 | 
				
			||||||
 | 
					            self.stdout.write(
 | 
				
			||||||
 | 
					                self.style.ERROR(
 | 
				
			||||||
 | 
					                    "Name already taken!"
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            exit(-1)
 | 
				
			||||||
 | 
					        pw = getpass("Password: ")
 | 
				
			||||||
 | 
					        pw_confirm = getpass("Confirm password : ")
 | 
				
			||||||
 | 
					        if pw != pw_confirm:
 | 
				
			||||||
 | 
					            self.stdout.write(
 | 
				
			||||||
 | 
					                self.style.ERROR(
 | 
				
			||||||
 | 
					                    "Passwords did not match!"
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            exit(-1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Create superuser
 | 
				
			||||||
 | 
					        superuser = User()
 | 
				
			||||||
 | 
					        superuser.username = username
 | 
				
			||||||
 | 
					        superuser.is_superuser = True
 | 
				
			||||||
 | 
					        superuser.is_staff = True
 | 
				
			||||||
 | 
					        superuser.set_password(pw)
 | 
				
			||||||
 | 
					        superuser.save()
 | 
				
			||||||
 | 
					        self.stdout.write(
 | 
				
			||||||
 | 
					            self.style.SUCCESS(
 | 
				
			||||||
 | 
					                "Superuser {} created".format(username)
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.__break_line()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init_role_types(self):
 | 
				
			||||||
 | 
					        """ Initializes available role types according to RoleTypeEnum
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.stdout.write(
 | 
				
			||||||
 | 
					            self.style.WARNING(
 | 
				
			||||||
 | 
					                "--- Role types ---"
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        for role_type_enum in RoleTypeEnum:
 | 
				
			||||||
 | 
					            role_type = RoleType.objects.get_or_create(
 | 
				
			||||||
 | 
					                type=role_type_enum.value
 | 
				
			||||||
 | 
					            )[0]
 | 
				
			||||||
 | 
					            self.stdout.write(
 | 
				
			||||||
 | 
					                self.style.SUCCESS(
 | 
				
			||||||
 | 
					                    CREATED_TEMPLATE.format(role_type.type)
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        self.__break_line()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init_test_organisation(self):
 | 
				
			||||||
 | 
					        """ Creates test organisations from predefined data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.stdout.write(
 | 
				
			||||||
 | 
					            self.style.WARNING(
 | 
				
			||||||
 | 
					                "--- Organisations ---"
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        for org in TEST_ORGANISATION_DATA:
 | 
				
			||||||
 | 
					            db_org = Organisation.objects.get_or_create(
 | 
				
			||||||
 | 
					                **org
 | 
				
			||||||
 | 
					            )[0]
 | 
				
			||||||
 | 
					            self.stdout.write(
 | 
				
			||||||
 | 
					                self.style.SUCCESS(
 | 
				
			||||||
 | 
					                    CREATED_TEMPLATE.format(db_org.name)
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        self.__break_line()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init_role_groups(self):
 | 
				
			||||||
 | 
					        """ Creates test role groups from predefined data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.stdout.write(
 | 
				
			||||||
 | 
					            self.style.WARNING(
 | 
				
			||||||
 | 
					                "--- Role Groups ---"
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        for group_data in TEST_ROLE_GROUPS_DATA:
 | 
				
			||||||
 | 
					            group_data["organisation"] = Organisation.objects.get(name=group_data["organisation"])
 | 
				
			||||||
 | 
					            group_data["role"] = RoleType.objects.get(type=group_data["role"])
 | 
				
			||||||
 | 
					            group = RoleGroup.objects.get_or_create(
 | 
				
			||||||
 | 
					                **group_data
 | 
				
			||||||
 | 
					            )[0]
 | 
				
			||||||
 | 
					            self.stdout.write(
 | 
				
			||||||
 | 
					                self.style.SUCCESS(
 | 
				
			||||||
 | 
					                    CREATED_TEMPLATE.format(group.name)
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        self.__break_line()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __break_line(self):
 | 
				
			||||||
 | 
					        """ Simply prints a line break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.stdout.write("\n")
 | 
				
			||||||
							
								
								
									
										58
									
								
								kspneo/management/commands/setup_test_data.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								kspneo/management/commands/setup_test_data.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,58 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Author: Michel Peltriaux
 | 
				
			||||||
 | 
					Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
				
			||||||
 | 
					Contact: michel.peltriaux@sgdnord.rlp.de
 | 
				
			||||||
 | 
					Created on: 15.12.20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					from organisation.enums import OrganisationTypeEnum, RoleTypeEnum
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TEST_ORGANISATION_DATA = [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "name": "Test_Official_1",
 | 
				
			||||||
 | 
					            "is_active": True,
 | 
				
			||||||
 | 
					            "is_deleted": False,
 | 
				
			||||||
 | 
					            "type": OrganisationTypeEnum.OFFICIAL.value,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "name": "Test_Official_2",
 | 
				
			||||||
 | 
					            "is_active": True,
 | 
				
			||||||
 | 
					            "is_deleted": False,
 | 
				
			||||||
 | 
					            "type": OrganisationTypeEnum.OFFICIAL.value,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "name": "Test_NGO_1",
 | 
				
			||||||
 | 
					            "is_active": True,
 | 
				
			||||||
 | 
					            "is_deleted": False,
 | 
				
			||||||
 | 
					            "type": OrganisationTypeEnum.NGO.value,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "name": "Test_Company_1",
 | 
				
			||||||
 | 
					            "is_active": True,
 | 
				
			||||||
 | 
					            "is_deleted": False,
 | 
				
			||||||
 | 
					            "type": OrganisationTypeEnum.COMPANY.value,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TEST_ROLE_GROUPS_DATA = [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "name": "Registration office Test_Official_1",
 | 
				
			||||||
 | 
					            "organisation": "Test_Official_1",
 | 
				
			||||||
 | 
					            "role": RoleTypeEnum.REGISTRATIONOFFICE.value,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "name": "Licensing authority Test_Official_1",
 | 
				
			||||||
 | 
					            "organisation": "Test_Official_1",
 | 
				
			||||||
 | 
					            "role": RoleTypeEnum.LICENSINGAUTHORITY.value,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "name": "Dataprovider Test_Official_2",
 | 
				
			||||||
 | 
					            "organisation": "Test_Official_2",
 | 
				
			||||||
 | 
					            "role": RoleTypeEnum.LICENSINGAUTHORITY.value,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "name": "Dataprovider Test_Company_1",
 | 
				
			||||||
 | 
					            "organisation": "Test_Company_1",
 | 
				
			||||||
 | 
					            "role": RoleTypeEnum.DATAPROVIDER.value,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
							
								
								
									
										104
									
								
								kspneo/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								kspneo/models.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,104 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Author: Michel Peltriaux
 | 
				
			||||||
 | 
					Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
				
			||||||
 | 
					Contact: michel.peltriaux@sgdnord.rlp.de
 | 
				
			||||||
 | 
					Created on: 17.11.20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					import uuid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.contrib.auth.models import User, Group
 | 
				
			||||||
 | 
					from django.db import models
 | 
				
			||||||
 | 
					from django.db.models import QuerySet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from organisation.enums import RoleTypeEnum
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BaseResource(models.Model):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    A basic resource model, which defines attributes for every derived model
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    id = models.UUIDField(
 | 
				
			||||||
 | 
					        primary_key=True,
 | 
				
			||||||
 | 
					        default=uuid.uuid4,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    is_active = models.BooleanField(default=True)
 | 
				
			||||||
 | 
					    is_deleted = models.BooleanField(default=False)
 | 
				
			||||||
 | 
					    created_on = models.DateTimeField(auto_now_add=True, null=True)
 | 
				
			||||||
 | 
					    created_by = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        abstract = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BaseObject(BaseResource):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    A basic object model, which specifies BaseResource.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Mainly used for intervention, compensation, ecoaccount
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    identifier = models.CharField(max_length=1000, null=True, blank=True)
 | 
				
			||||||
 | 
					    title = models.CharField(max_length=1000, null=True, blank=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        abstract = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Deadline(BaseResource):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Defines a deadline, which can be used to define dates with a semantic meaning
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    type = models.CharField(max_length=500, null=True, blank=True)
 | 
				
			||||||
 | 
					    date = models.DateField(null=True, blank=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self):
 | 
				
			||||||
 | 
					        return self.type
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Document(BaseResource):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Documents can be attached to process, compensation or intervention for uploading legal documents or pictures.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    date_of_creation = models.DateField()
 | 
				
			||||||
 | 
					    document = models.FileField()
 | 
				
			||||||
 | 
					    comment = models.TextField()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class RoleType(BaseResource):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Defines different role types
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    type = models.CharField(max_length=255, choices=RoleTypeEnum.as_choices(drop_empty_choice=True), unique=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self):
 | 
				
			||||||
 | 
					        return self.type
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class RoleGroup(Group):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Role groups are specialized groups which hold information on which users are related to a certain organisation and
 | 
				
			||||||
 | 
					    a role
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    organisation = models.ForeignKey("organisation.Organisation", on_delete=models.CASCADE)
 | 
				
			||||||
 | 
					    role = models.ForeignKey(RoleType, on_delete=models.CASCADE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        unique_together = [
 | 
				
			||||||
 | 
					            ["organisation", "role", ]
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    def get_users_role_groups(user: User) -> QuerySet:
 | 
				
			||||||
 | 
					        """ Get all role groups of a given user
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            user (User): The user
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					            qs (QuerySet)
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        if user.is_anonymous:
 | 
				
			||||||
 | 
					            return RoleGroup.objects.none()
 | 
				
			||||||
 | 
					        return RoleGroup.objects.filter(
 | 
				
			||||||
 | 
					            user=user
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
							
								
								
									
										50
									
								
								kspneo/settings.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								kspneo/settings.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,50 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Django settings for konova project.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Generated by 'django-admin startproject' using Django 3.1.2.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For more information on this file, see
 | 
				
			||||||
 | 
					https://docs.djangoproject.com/en/3.1/topics/settings/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For the full list of settings and their values, see
 | 
				
			||||||
 | 
					https://docs.djangoproject.com/en/3.1/ref/settings/
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Load other settings
 | 
				
			||||||
 | 
					from konova.sub_settings.django_settings import *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Num of days if user enables Remember-me on login
 | 
				
			||||||
 | 
					KEEP_LOGGED_DURATION = 30
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# German DateTime string format
 | 
				
			||||||
 | 
					STRF_DATE_TIME = "%d.%m.%Y %H:%M:%S"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Tables
 | 
				
			||||||
 | 
					RESULTS_PER_PAGE_PARAM = "rpp"
 | 
				
			||||||
 | 
					PAGE_PARAM = "page"
 | 
				
			||||||
 | 
					PAGE_SIZE_OPTIONS = [5, 10, 15, 20, 25, 30, 50, 75, 100]
 | 
				
			||||||
 | 
					PAGE_SIZE_OPTIONS_TUPLES = [
 | 
				
			||||||
 | 
					    (5, 5),
 | 
				
			||||||
 | 
					    (10, 10),
 | 
				
			||||||
 | 
					    (15, 15),
 | 
				
			||||||
 | 
					    (20, 20),
 | 
				
			||||||
 | 
					    (25, 25),
 | 
				
			||||||
 | 
					    (30, 30),
 | 
				
			||||||
 | 
					    (50, 50),
 | 
				
			||||||
 | 
					    (75, 75),
 | 
				
			||||||
 | 
					    (100, 100),
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					PAGE_SIZE_DEFAULT = 5
 | 
				
			||||||
 | 
					PAGE_SIZE_MAX = 100
 | 
				
			||||||
 | 
					PAGE_DEFAULT = 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# SSO settings
 | 
				
			||||||
 | 
					SSO_SERVER_BASE = "http://127.0.0.1:8000/"
 | 
				
			||||||
 | 
					SSO_SERVER = "{}sso/".format(SSO_SERVER_BASE)
 | 
				
			||||||
 | 
					SSO_PRIVATE_KEY = "CHANGE_ME"
 | 
				
			||||||
 | 
					SSO_PUBLIC_KEY = "CHANGE_ME"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# MAPS
 | 
				
			||||||
 | 
					DEFAULT_LAT = 50.00
 | 
				
			||||||
 | 
					DEFAULT_LON = 7.00
 | 
				
			||||||
 | 
					DEFAULT_ZOOM = 8.0
 | 
				
			||||||
							
								
								
									
										57
									
								
								kspneo/static/css/kspneo.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								kspneo/static/css/kspneo.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,57 @@
 | 
				
			|||||||
 | 
					.body-content{
 | 
				
			||||||
 | 
					    min-height: 75vh;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.note{
 | 
				
			||||||
 | 
					    font-size: 0.75rem;
 | 
				
			||||||
 | 
					    color: lightgray;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.label-required{
 | 
				
			||||||
 | 
					    color: red;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					table{
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.footer{
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.error{
 | 
				
			||||||
 | 
					    color: #D8000C !important;
 | 
				
			||||||
 | 
					    background-color: #FFBABA !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					i.true{
 | 
				
			||||||
 | 
					    color: green;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					i.false{
 | 
				
			||||||
 | 
					    color: red;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.button{
 | 
				
			||||||
 | 
					    margin: 0.5625rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.action-col{
 | 
				
			||||||
 | 
					    max-width: 8rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.action-col .button{
 | 
				
			||||||
 | 
					    margin: 0.1rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.user-role{
 | 
				
			||||||
 | 
					    background: url('../images/menu-bg.png') repeat #871d33;
 | 
				
			||||||
 | 
					    color: white;
 | 
				
			||||||
 | 
					    padding: 0.5rem 0;
 | 
				
			||||||
 | 
					    border-bottom: 4px solid #8e8e8e;
 | 
				
			||||||
 | 
					    text-align: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.user-role > a {
 | 
				
			||||||
 | 
					    color: white;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										43
									
								
								kspneo/static/css/messages.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								kspneo/static/css/messages.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,43 @@
 | 
				
			|||||||
 | 
					.info, .success, .warning, .error, .validation {
 | 
				
			||||||
 | 
					border: 1px solid;
 | 
				
			||||||
 | 
					margin: 10px 0px;
 | 
				
			||||||
 | 
					padding:15px 10px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.info:hover,
 | 
				
			||||||
 | 
					.success:hover,
 | 
				
			||||||
 | 
					.warning:hover,
 | 
				
			||||||
 | 
					.error:hover,
 | 
				
			||||||
 | 
					.validation:hover {
 | 
				
			||||||
 | 
					    cursor: default;
 | 
				
			||||||
 | 
					    filter: brightness(1.2);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.info {
 | 
				
			||||||
 | 
					color: #00529B;
 | 
				
			||||||
 | 
					background-color: #BDE5F8;
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					background-image: url('../images/knobs/info.png');
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.success {
 | 
				
			||||||
 | 
					color: #4F8A10;
 | 
				
			||||||
 | 
					background-color: #DFF2BF;
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					background-image:url('../images/knobs/success.png');
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.warning {
 | 
				
			||||||
 | 
					color: #9F6000;
 | 
				
			||||||
 | 
					background-color: #FEEFB3;
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					background-image: url('../images/knobs/warning.png');
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.error {
 | 
				
			||||||
 | 
					color: #D8000C;
 | 
				
			||||||
 | 
					background-color: #FFBABA;
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					background-image: url('../images/knobs/error.png');
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										9877
									
								
								kspneo/static/css/mulewf.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9877
									
								
								kspneo/static/css/mulewf.css
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								kspneo/static/fonts/rlp-icons.woff
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								kspneo/static/fonts/rlp-icons.woff
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								kspneo/static/images/csm_rlp-logo_8de1241483.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								kspneo/static/images/csm_rlp-logo_8de1241483.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 6.3 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								kspneo/static/images/header-bg.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								kspneo/static/images/header-bg.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 2.2 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								kspneo/static/images/menu-bg.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								kspneo/static/images/menu-bg.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 935 B  | 
							
								
								
									
										
											BIN
										
									
								
								kspneo/static/images/rlp-logos-MUEEF.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								kspneo/static/images/rlp-logos-MUEEF.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 10 KiB  | 
							
								
								
									
										2
									
								
								kspneo/static/js/jquery-3.5.1.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								kspneo/static/js/jquery-3.5.1.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										13
									
								
								kspneo/static/js/jquery-ui.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								kspneo/static/js/jquery-ui.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										6445
									
								
								kspneo/static/js/mulewf.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6445
									
								
								kspneo/static/js/mulewf.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										12
									
								
								kspneo/sub_settings/context_settings.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								kspneo/sub_settings/context_settings.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Author: Michel Peltriaux
 | 
				
			||||||
 | 
					Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
				
			||||||
 | 
					Contact: michel.peltriaux@sgdnord.rlp.de
 | 
				
			||||||
 | 
					Created on: 16.11.20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					BASE_TITLE = "konova"
 | 
				
			||||||
 | 
					BASE_FRONTEND_TITLE = "Kompensationsverzeichnis Service Portal"
 | 
				
			||||||
 | 
					WIKI_URL = "https://dienste.naturschutz.rlp.de/doku/doku.php?id=ksp:start"
 | 
				
			||||||
 | 
					RENDER_HEADER = False
 | 
				
			||||||
							
								
								
									
										230
									
								
								kspneo/sub_settings/django_settings.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										230
									
								
								kspneo/sub_settings/django_settings.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,230 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Django settings for konova project.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Generated by 'django-admin startproject' using Django 3.1.3.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For more information on this file, see
 | 
				
			||||||
 | 
					https://docs.djangoproject.com/en/3.1/topics/settings/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For the full list of settings and their values, see
 | 
				
			||||||
 | 
					https://docs.djangoproject.com/en/3.1/ref/settings/
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					from pathlib import Path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Build paths inside the project like this: BASE_DIR / 'subdir'.
 | 
				
			||||||
 | 
					BASE_DIR = os.path.dirname(
 | 
				
			||||||
 | 
					    os.path.dirname(
 | 
				
			||||||
 | 
					        os.path.dirname(
 | 
				
			||||||
 | 
					            os.path.abspath(
 | 
				
			||||||
 | 
					                __file__
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Quick-start development settings - unsuitable for production
 | 
				
			||||||
 | 
					# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# SECURITY WARNING: keep the secret key used in production secret!
 | 
				
			||||||
 | 
					SECRET_KEY = '5=9-)2)h$u9=!zrhia9=lj-2#cpcb8=#$7y+)l$5tto$3q(n_+'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# SECURITY WARNING: don't run with debug turned on in production!
 | 
				
			||||||
 | 
					DEBUG = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ALLOWED_HOSTS = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Authentication settings
 | 
				
			||||||
 | 
					LOGIN_URL = "/login/"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Session settings
 | 
				
			||||||
 | 
					SESSION_COOKIE_AGE = 30 * 60  # 30 minutes
 | 
				
			||||||
 | 
					SESSION_SAVE_EVERY_REQUEST = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Application definition
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					INSTALLED_APPS = [
 | 
				
			||||||
 | 
					    'dal',
 | 
				
			||||||
 | 
					    'dal_select2',
 | 
				
			||||||
 | 
					    'django.contrib.admin',
 | 
				
			||||||
 | 
					    'django.contrib.auth',
 | 
				
			||||||
 | 
					    'django.contrib.contenttypes',
 | 
				
			||||||
 | 
					    'django.contrib.sessions',
 | 
				
			||||||
 | 
					    'django.contrib.messages',
 | 
				
			||||||
 | 
					    'django.contrib.staticfiles',
 | 
				
			||||||
 | 
					    'django.contrib.gis',
 | 
				
			||||||
 | 
					    'simple_sso.sso_server',
 | 
				
			||||||
 | 
					    'django_tables2',
 | 
				
			||||||
 | 
					    'fontawesome_5',
 | 
				
			||||||
 | 
					    'konova',
 | 
				
			||||||
 | 
					    'compensation',
 | 
				
			||||||
 | 
					    'intervention',
 | 
				
			||||||
 | 
					    'process',
 | 
				
			||||||
 | 
					    'organisation',
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					if DEBUG:
 | 
				
			||||||
 | 
					    INSTALLED_APPS += [
 | 
				
			||||||
 | 
					        'debug_toolbar',
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MIDDLEWARE = [
 | 
				
			||||||
 | 
					    'django.middleware.security.SecurityMiddleware',
 | 
				
			||||||
 | 
					    'django.contrib.sessions.middleware.SessionMiddleware',
 | 
				
			||||||
 | 
					    'django.middleware.common.CommonMiddleware',
 | 
				
			||||||
 | 
					    'django.middleware.csrf.CsrfViewMiddleware',
 | 
				
			||||||
 | 
					    'django.contrib.auth.middleware.AuthenticationMiddleware',
 | 
				
			||||||
 | 
					    'django.contrib.messages.middleware.MessageMiddleware',
 | 
				
			||||||
 | 
					    'django.middleware.clickjacking.XFrameOptionsMiddleware',
 | 
				
			||||||
 | 
					    "django.middleware.locale.LocaleMiddleware",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					if DEBUG:
 | 
				
			||||||
 | 
					    MIDDLEWARE += [
 | 
				
			||||||
 | 
					        "debug_toolbar.middleware.DebugToolbarMiddleware",
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ROOT_URLCONF = 'konova.urls'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TEMPLATES = [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        'BACKEND': 'django.template.backends.django.DjangoTemplates',
 | 
				
			||||||
 | 
					        'DIRS': [
 | 
				
			||||||
 | 
					            os.path.join(BASE_DIR, "templates"),
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        'APP_DIRS': True,
 | 
				
			||||||
 | 
					        'OPTIONS': {
 | 
				
			||||||
 | 
					            'context_processors': [
 | 
				
			||||||
 | 
					                'django.template.context_processors.debug',
 | 
				
			||||||
 | 
					                'django.template.context_processors.request',
 | 
				
			||||||
 | 
					                'django.contrib.auth.context_processors.auth',
 | 
				
			||||||
 | 
					                'django.contrib.messages.context_processors.messages',
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					WSGI_APPLICATION = 'konova.wsgi.application'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Database
 | 
				
			||||||
 | 
					# https://docs.djangoproject.com/en/3.1/ref/settings/#databases
 | 
				
			||||||
 | 
					DATABASES = {
 | 
				
			||||||
 | 
					    'default': {
 | 
				
			||||||
 | 
					        'ENGINE': 'django.contrib.gis.db.backends.postgis',
 | 
				
			||||||
 | 
					        'NAME': 'konova',
 | 
				
			||||||
 | 
					        'USER': 'postgres',
 | 
				
			||||||
 | 
					        'HOST': '127.0.0.1',
 | 
				
			||||||
 | 
					        'PORT': '5432',
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Password validation
 | 
				
			||||||
 | 
					# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AUTH_PASSWORD_VALIDATORS = [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Internationalization
 | 
				
			||||||
 | 
					# https://docs.djangoproject.com/en/3.1/topics/i18n/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LANGUAGE_CODE = 'en-us'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEFAULT_DATE_TIME_FORMAT = 'YYYY-MM-DD hh:mm:ss'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TIME_ZONE = 'Europe/Berlin'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					USE_I18N = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					USE_L10N = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					USE_TZ = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LOCALE_PATHS = (
 | 
				
			||||||
 | 
					    os.path.join(BASE_DIR, 'locale'),
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Static files (CSS, JavaScript, Images)
 | 
				
			||||||
 | 
					# https://docs.djangoproject.com/en/3.1/howto/static-files/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					STATIC_URL = '/static/'
 | 
				
			||||||
 | 
					STATIC_ROOT = os.path.join(BASE_DIR, "static")
 | 
				
			||||||
 | 
					STATICFILES_DIRS = [
 | 
				
			||||||
 | 
					    os.path.join(BASE_DIR, 'konova/static'),
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# DJANGO DEBUG TOOLBAR
 | 
				
			||||||
 | 
					INTERNAL_IPS = [
 | 
				
			||||||
 | 
					    "127.0.0.1"
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					DEBUG_TOOLBAR_CONFIG = {
 | 
				
			||||||
 | 
					    "DISABLE_PANELS": {
 | 
				
			||||||
 | 
					        'debug_toolbar.panels.versions.VersionsPanel',
 | 
				
			||||||
 | 
					        'debug_toolbar.panels.timer.TimerPanel',
 | 
				
			||||||
 | 
					        'debug_toolbar.panels.settings.SettingsPanel',
 | 
				
			||||||
 | 
					        'debug_toolbar.panels.headers.HeadersPanel',
 | 
				
			||||||
 | 
					        'debug_toolbar.panels.request.RequestPanel',
 | 
				
			||||||
 | 
					        'debug_toolbar.panels.sql.SQLPanel',
 | 
				
			||||||
 | 
					        'debug_toolbar.panels.staticfiles.StaticFilesPanel',
 | 
				
			||||||
 | 
					        'debug_toolbar.panels.templates.TemplatesPanel',
 | 
				
			||||||
 | 
					        'debug_toolbar.panels.cache.CachePanel',
 | 
				
			||||||
 | 
					        'debug_toolbar.panels.signals.SignalsPanel',
 | 
				
			||||||
 | 
					        'debug_toolbar.panels.logging.LoggingPanel',
 | 
				
			||||||
 | 
					        'debug_toolbar.panels.redirects.RedirectsPanel',
 | 
				
			||||||
 | 
					        'debug_toolbar.panels.profiling.ProfilingPanel',
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# EMAIL (see https://docs.djangoproject.com/en/dev/topics/email/)
 | 
				
			||||||
 | 
					DEFAULT_FROM_EMAIL = "bot@arneo.de"  # The default email address for the 'from' element
 | 
				
			||||||
 | 
					EMAIL_HOST = "localhost"
 | 
				
			||||||
 | 
					EMAIL_PORT = "1025"
 | 
				
			||||||
 | 
					#EMAIL_HOST_USER = ""
 | 
				
			||||||
 | 
					#EMAIL_HOST_PASSWORD = ""
 | 
				
			||||||
 | 
					EMAIL_USE_TLS = False
 | 
				
			||||||
 | 
					EMAIL_USE_SSL = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# LOGGING
 | 
				
			||||||
 | 
					BASIC_LOGGER = "logger"
 | 
				
			||||||
 | 
					LOGGING = {
 | 
				
			||||||
 | 
					    'version': 1,
 | 
				
			||||||
 | 
					    'disable_existing_loggers': False,
 | 
				
			||||||
 | 
					    'formatters': {
 | 
				
			||||||
 | 
					        'verbose': {
 | 
				
			||||||
 | 
					            'format': '{levelname} {asctime} {module}: {message}',
 | 
				
			||||||
 | 
					            'style': '{',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'simple': {
 | 
				
			||||||
 | 
					            'format': '{levelname} {message}',
 | 
				
			||||||
 | 
					            'style': '{',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    'handlers': {
 | 
				
			||||||
 | 
					        'log_to_file': {
 | 
				
			||||||
 | 
					            'level': 'DEBUG',
 | 
				
			||||||
 | 
					            'class': 'logging.handlers.RotatingFileHandler',
 | 
				
			||||||
 | 
					            'filename': '{}/logs/error.log'.format(BASE_DIR),
 | 
				
			||||||
 | 
					            'maxBytes': 1024*1024*5, # 5 MB
 | 
				
			||||||
 | 
					            'backupCount': 5,
 | 
				
			||||||
 | 
					            'formatter': 'verbose',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    'loggers': {
 | 
				
			||||||
 | 
					        BASIC_LOGGER: {
 | 
				
			||||||
 | 
					            'handlers': ['log_to_file'],
 | 
				
			||||||
 | 
					            'level': 'INFO',
 | 
				
			||||||
 | 
					            'propagate': True,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										5
									
								
								kspneo/templates/kspneo/choiceColumnForm.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								kspneo/templates/kspneo/choiceColumnForm.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					<form action="{{ form.action_url }}" method="post">
 | 
				
			||||||
 | 
					    {% csrf_token %}
 | 
				
			||||||
 | 
					    {{ form.as_p }}
 | 
				
			||||||
 | 
					</form>
 | 
				
			||||||
							
								
								
									
										8
									
								
								kspneo/templates/kspneo/form.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								kspneo/templates/kspneo/form.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					{% extends 'base.html' %}
 | 
				
			||||||
 | 
					{% load i18n %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block body %}
 | 
				
			||||||
 | 
					<div class="column">
 | 
				
			||||||
 | 
					    {% include 'generic_table_form.html' %}
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
							
								
								
									
										44
									
								
								kspneo/templates/kspneo/home.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								kspneo/templates/kspneo/home.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					{% extends 'base.html' %}
 | 
				
			||||||
 | 
					{% load i18n %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block body_middle %}
 | 
				
			||||||
 | 
					    <h1>Kompensationsverzeichnis</h1>
 | 
				
			||||||
 | 
					    <h2>Service Portal</h2>
 | 
				
			||||||
 | 
					    <hr>
 | 
				
			||||||
 | 
					{% if user.is_anonymous %}
 | 
				
			||||||
 | 
					    <a href="{% url 'simple-sso-login' %}">
 | 
				
			||||||
 | 
					        <button class="button middle">
 | 
				
			||||||
 | 
					            {% trans 'Proceed with login' %}
 | 
				
			||||||
 | 
					        </button>
 | 
				
			||||||
 | 
					    </a>
 | 
				
			||||||
 | 
					{% else %}
 | 
				
			||||||
 | 
					    <article>
 | 
				
			||||||
 | 
					        {% trans 'Logged in as' %}  <strong>{{ user.username }}</strong>
 | 
				
			||||||
 | 
					        <br>
 | 
				
			||||||
 | 
					        {% trans 'Last login on' %} {{ user.last_login }}
 | 
				
			||||||
 | 
					    </article>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <form action="{{form.action_url}}" method="post">
 | 
				
			||||||
 | 
					        {% csrf_token %}
 | 
				
			||||||
 | 
					        <table>
 | 
				
			||||||
 | 
					            {% comment %}
 | 
				
			||||||
 | 
					            This is an alternative to using the <article></article>
 | 
				
			||||||
 | 
					            <tr>
 | 
				
			||||||
 | 
					                <td>{% trans 'Logged in as' %}</td>
 | 
				
			||||||
 | 
					                <td><strong>{{ user.username }}</strong></td>
 | 
				
			||||||
 | 
					            </tr>
 | 
				
			||||||
 | 
					            <tr>
 | 
				
			||||||
 | 
					                <td>{% trans 'Last login on' %}</td>
 | 
				
			||||||
 | 
					                <td><strong>{{ user.last_login }}</strong></td>
 | 
				
			||||||
 | 
					            </tr>
 | 
				
			||||||
 | 
					            {% endcomment %}
 | 
				
			||||||
 | 
					            {% for field in form %}
 | 
				
			||||||
 | 
					            <tr>
 | 
				
			||||||
 | 
					                <td>{{ field.label }}</td>
 | 
				
			||||||
 | 
					                <td>{{ field }}</td>
 | 
				
			||||||
 | 
					            </tr>
 | 
				
			||||||
 | 
					            {% endfor %}
 | 
				
			||||||
 | 
					        </table>
 | 
				
			||||||
 | 
					    </form>
 | 
				
			||||||
 | 
					{% endif %}
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
							
								
								
									
										7
									
								
								kspneo/templatetags/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								kspneo/templatetags/__init__.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Author: Michel Peltriaux
 | 
				
			||||||
 | 
					Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
				
			||||||
 | 
					Contact: michel.peltriaux@sgdnord.rlp.de
 | 
				
			||||||
 | 
					Created on: 04.12.20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
							
								
								
									
										17
									
								
								kspneo/templatetags/custom_tags.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								kspneo/templatetags/custom_tags.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Author: Michel Peltriaux
 | 
				
			||||||
 | 
					Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
				
			||||||
 | 
					Contact: michel.peltriaux@sgdnord.rlp.de
 | 
				
			||||||
 | 
					Created on: 04.12.20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					from django import template
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from process.settings import PROCESS_STATE_STRINGS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					register = template.Library()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@register.filter
 | 
				
			||||||
 | 
					def resolve_process_state(value):
 | 
				
			||||||
 | 
					    return PROCESS_STATE_STRINGS.get(value, None)
 | 
				
			||||||
							
								
								
									
										47
									
								
								kspneo/urls.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								kspneo/urls.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					"""konova URL Configuration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The `urlpatterns` list routes URLs to views. For more information please see:
 | 
				
			||||||
 | 
					    https://docs.djangoproject.com/en/3.1/topics/http/urls/
 | 
				
			||||||
 | 
					Examples:
 | 
				
			||||||
 | 
					Function views
 | 
				
			||||||
 | 
					    1. Add an import:  from my_app import views
 | 
				
			||||||
 | 
					    2. Add a URL to urlpatterns:  path('', views.home, name='home')
 | 
				
			||||||
 | 
					Class-based views
 | 
				
			||||||
 | 
					    1. Add an import:  from other_app.views import Home
 | 
				
			||||||
 | 
					    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
 | 
				
			||||||
 | 
					Including another URLconf
 | 
				
			||||||
 | 
					    1. Import the include() function: from django.urls import include, path
 | 
				
			||||||
 | 
					    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					import debug_toolbar
 | 
				
			||||||
 | 
					from django.contrib import admin
 | 
				
			||||||
 | 
					from django.urls import path, include
 | 
				
			||||||
 | 
					from simple_sso.sso_client.client import Client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from konova.autocompletes import OrganisationAutocomplete, NonOfficialOrganisationAutocomplete
 | 
				
			||||||
 | 
					from konova.settings import SSO_SERVER, SSO_PUBLIC_KEY, SSO_PRIVATE_KEY, DEBUG
 | 
				
			||||||
 | 
					from konova.views import logout_view, home_view
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					sso_client = Client(SSO_SERVER, SSO_PUBLIC_KEY, SSO_PRIVATE_KEY)
 | 
				
			||||||
 | 
					urlpatterns = [
 | 
				
			||||||
 | 
					    path('admin/', admin.site.urls),
 | 
				
			||||||
 | 
					    path('login/', include(sso_client.get_urls())),
 | 
				
			||||||
 | 
					    path('logout/', logout_view, name="logout"),
 | 
				
			||||||
 | 
					    path('', home_view, name="home"),
 | 
				
			||||||
 | 
					    path('process/', include("process.urls")),
 | 
				
			||||||
 | 
					    path('intervention/', include("intervention.urls")),
 | 
				
			||||||
 | 
					    path('compensation/', include("compensation.urls")),
 | 
				
			||||||
 | 
					    path('eco-account/', include("process.urls")),
 | 
				
			||||||
 | 
					    path('ema/', include("process.urls")),
 | 
				
			||||||
 | 
					    path('organisation/', include("organisation.urls")),
 | 
				
			||||||
 | 
					    path('user/', include("process.urls")),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Autocomplete paths
 | 
				
			||||||
 | 
					    path("atcmplt/orgs", OrganisationAutocomplete.as_view(), name="orgs-autocomplete"),
 | 
				
			||||||
 | 
					    path("atcmplt/orgs/other", NonOfficialOrganisationAutocomplete.as_view(), name="other-orgs-autocomplete"),
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if DEBUG:
 | 
				
			||||||
 | 
					    urlpatterns += [
 | 
				
			||||||
 | 
					        path('__debug__/', include(debug_toolbar.urls)),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
							
								
								
									
										22
									
								
								kspneo/utils/generators.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								kspneo/utils/generators.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Author: Michel Peltriaux
 | 
				
			||||||
 | 
					Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
				
			||||||
 | 
					Contact: michel.peltriaux@sgdnord.rlp.de
 | 
				
			||||||
 | 
					Created on: 09.11.20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					import random
 | 
				
			||||||
 | 
					import string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def generate_random_string(length: int, only_numbers: bool = False) -> str:
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Generates a random string of variable length
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    if only_numbers:
 | 
				
			||||||
 | 
					        elements = string.digits
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        elements = string.ascii_letters
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ret_val = "".join(random.choice(elements) for i in range(length))
 | 
				
			||||||
 | 
					    return ret_val
 | 
				
			||||||
							
								
								
									
										52
									
								
								kspneo/utils/mailer.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								kspneo/utils/mailer.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,52 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Author: Michel Peltriaux
 | 
				
			||||||
 | 
					Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
				
			||||||
 | 
					Contact: michel.peltriaux@sgdnord.rlp.de
 | 
				
			||||||
 | 
					Created on: 09.11.20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					import logging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.core.mail import send_mail
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from konova.sub_settings.django_settings import DEFAULT_FROM_EMAIL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					logger = logging.getLogger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Mailer:
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    A wrapper for the django internal mailing functionality
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    from_mail = None
 | 
				
			||||||
 | 
					    to_mail = []
 | 
				
			||||||
 | 
					    fail_silently = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Optional. Can be changed using the constructor to authenticate on the smtp server using other credentials
 | 
				
			||||||
 | 
					    auth_user = None
 | 
				
			||||||
 | 
					    auth_password = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, to_mail: list, from_mail: str = DEFAULT_FROM_EMAIL, auth_user: str = None, auth_password: str = None, fail_silently: bool = False):
 | 
				
			||||||
 | 
					        # Make sure given to_mail parameter is a list
 | 
				
			||||||
 | 
					        if isinstance(to_mail, str):
 | 
				
			||||||
 | 
					            to_mail = [to_mail]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.from_mail = from_mail
 | 
				
			||||||
 | 
					        self.to_mail = to_mail
 | 
				
			||||||
 | 
					        self.fail_silently = fail_silently
 | 
				
			||||||
 | 
					        self.auth_user = auth_user
 | 
				
			||||||
 | 
					        self.auth_password = auth_password
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def send(self, subject: str, msg: str):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Sends a mail with subject and message
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        return send_mail(
 | 
				
			||||||
 | 
					            subject=subject,
 | 
				
			||||||
 | 
					            message=msg,
 | 
				
			||||||
 | 
					            from_email=self.from_mail,
 | 
				
			||||||
 | 
					            recipient_list=self.to_mail,
 | 
				
			||||||
 | 
					            fail_silently=self.fail_silently,
 | 
				
			||||||
 | 
					            auth_user=self.auth_user,
 | 
				
			||||||
 | 
					            auth_password=self.auth_password
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
							
								
								
									
										45
									
								
								kspneo/utils/session.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								kspneo/utils/session.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,45 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Author: Michel Peltriaux
 | 
				
			||||||
 | 
					Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
				
			||||||
 | 
					Contact: michel.peltriaux@sgdnord.rlp.de
 | 
				
			||||||
 | 
					Created on: 09.12.20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					from django.http import HttpRequest
 | 
				
			||||||
 | 
					from idna import unicode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from konova.models import RoleGroup
 | 
				
			||||||
 | 
					from organisation.settings import ROLE_TYPE_STRINGS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CURRENT_ROLE_ID = "current_role"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def set_session_user_role(request: HttpRequest, role_group: RoleGroup) -> dict:
 | 
				
			||||||
 | 
					    """ Set the user session to an active role
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Args:
 | 
				
			||||||
 | 
					        request (HttpRequest): The user request
 | 
				
			||||||
 | 
					        role_group (RoleGroup): The selected role group
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Returns:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    current_role = {}
 | 
				
			||||||
 | 
					    if role_group is not None:
 | 
				
			||||||
 | 
					        current_role["type"] = unicode(ROLE_TYPE_STRINGS.get(role_group.role.type))
 | 
				
			||||||
 | 
					        current_role["org"] = role_group.organisation.__str__()
 | 
				
			||||||
 | 
					        current_role["id"] = role_group.id
 | 
				
			||||||
 | 
					        request.session[CURRENT_ROLE_ID] = current_role
 | 
				
			||||||
 | 
					    return current_role
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_session_user_role(request: HttpRequest) -> dict:
 | 
				
			||||||
 | 
					    """ Returns the current role chosen by a user for this session
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Args:
 | 
				
			||||||
 | 
					        request (HttpRequest): The used request
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Returns:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    return request.session.get(CURRENT_ROLE_ID, {})
 | 
				
			||||||
							
								
								
									
										121
									
								
								kspneo/utils/tables.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								kspneo/utils/tables.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,121 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Author: Michel Peltriaux
 | 
				
			||||||
 | 
					Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
				
			||||||
 | 
					Contact: michel.peltriaux@sgdnord.rlp.de
 | 
				
			||||||
 | 
					Created on: 25.11.20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					import uuid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django import forms
 | 
				
			||||||
 | 
					from django.core.paginator import PageNotAnInteger, EmptyPage
 | 
				
			||||||
 | 
					from django.http import HttpRequest
 | 
				
			||||||
 | 
					from django.utils.html import format_html
 | 
				
			||||||
 | 
					import django_tables2 as tables
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from konova.forms import BaseForm
 | 
				
			||||||
 | 
					from konova.settings import PAGE_SIZE_DEFAULT, PAGE_PARAM, RESULTS_PER_PAGE_PARAM, PAGE_SIZE_OPTIONS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BaseTable(tables.tables.Table):
 | 
				
			||||||
 | 
					    results_per_page_choices = PAGE_SIZE_OPTIONS
 | 
				
			||||||
 | 
					    results_per_page_chosen = None
 | 
				
			||||||
 | 
					    results_per_page_parameter = RESULTS_PER_PAGE_PARAM
 | 
				
			||||||
 | 
					    add_new_entries = True
 | 
				
			||||||
 | 
					    add_new_url = None
 | 
				
			||||||
 | 
					    title = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, request: HttpRequest = None, filter_set=None, queryset=None, *args, **kwargs):
 | 
				
			||||||
 | 
					        self.user = request.user or None
 | 
				
			||||||
 | 
					        if filter_set is not None:
 | 
				
			||||||
 | 
					            queryset = filter_set.qs
 | 
				
			||||||
 | 
					        kwargs["data"] = queryset
 | 
				
			||||||
 | 
					        kwargs["request"] = request
 | 
				
			||||||
 | 
					        super().__init__(*args, **kwargs)
 | 
				
			||||||
 | 
					        self.results_per_page_chosen = int(request.GET.get(RESULTS_PER_PAGE_PARAM, PAGE_SIZE_DEFAULT))
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            self.paginate(
 | 
				
			||||||
 | 
					                page=request.GET.get(PAGE_PARAM, 1),
 | 
				
			||||||
 | 
					                per_page=self.results_per_page_chosen,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        except (PageNotAnInteger, EmptyPage) as e:
 | 
				
			||||||
 | 
					            self.paginate(
 | 
				
			||||||
 | 
					                page=1,
 | 
				
			||||||
 | 
					                per_page=self.results_per_page_chosen,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def render_link(self, tooltip: str, href: str, txt: str, new_tab: bool = False):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Returns an <a> html element using given parameters
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        new_tab = "_blank" if new_tab else "_self"
 | 
				
			||||||
 | 
					        return format_html(
 | 
				
			||||||
 | 
					            "<a href={} target='{}' title='{}'>{}</a>",
 | 
				
			||||||
 | 
					            href,
 | 
				
			||||||
 | 
					            new_tab,
 | 
				
			||||||
 | 
					            tooltip,
 | 
				
			||||||
 | 
					            txt,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def render_delete_btn(self, tooltip: str = None, href: str = None):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Returns a remover icon with <a> support as html element using given parameters
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        return format_html(
 | 
				
			||||||
 | 
					            "<a href={} title='{}'><button class='button small'><em class='fas fa-trash-alt'></em></button></a>",
 | 
				
			||||||
 | 
					            href,
 | 
				
			||||||
 | 
					            tooltip,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def render_edit_btn(self, tooltip: str = None, href: str = None):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Returns a remover icon with <a> support as html element using given parameters
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        return format_html(
 | 
				
			||||||
 | 
					            "<a href={} title='{}'><button class='button small'><em class='fas fa-edit'></em></button></a>",
 | 
				
			||||||
 | 
					            href,
 | 
				
			||||||
 | 
					            tooltip,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def render_open_btn(self, tooltip: str = None, href: str = None, new_tab: bool = False):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Returns a remover icon with <a> support as html element using given parameters
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        return format_html(
 | 
				
			||||||
 | 
					            "<a href={} title='{}' target='{}'><button class='button small'><em class='fas fa-sign-in-alt'></em></button></a>",
 | 
				
			||||||
 | 
					            href,
 | 
				
			||||||
 | 
					            tooltip,
 | 
				
			||||||
 | 
					            "_blank" if new_tab else ""
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def render_boolean(self, tooltip: str = None, val: bool = False):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Returns a remover icon with <a> support as html element using given parameters
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        icon = "fas fa-check-circle true" if val else "fas fa-times-circle false"
 | 
				
			||||||
 | 
					        return format_html(
 | 
				
			||||||
 | 
					            "<em title='{}' class='{}'></em>",
 | 
				
			||||||
 | 
					            tooltip,
 | 
				
			||||||
 | 
					            icon
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ChoicesColumnForm(BaseForm):
 | 
				
			||||||
 | 
					    select = forms.ChoiceField(
 | 
				
			||||||
 | 
					        choices=[],
 | 
				
			||||||
 | 
					        label="",
 | 
				
			||||||
 | 
					        label_suffix="",
 | 
				
			||||||
 | 
					        widget=forms.Select(
 | 
				
			||||||
 | 
					            attrs={
 | 
				
			||||||
 | 
					                "onchange": "submit();",
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, *args, **kwargs):
 | 
				
			||||||
 | 
					        self.action_url = kwargs.pop("action_url", None)
 | 
				
			||||||
 | 
					        self.choices = kwargs.pop("choices", [])
 | 
				
			||||||
 | 
					        super().__init__(*args, **kwargs)
 | 
				
			||||||
 | 
					        self.auto_id += "_" + str(uuid.uuid4())
 | 
				
			||||||
 | 
					        if len(self.choices) > 0:
 | 
				
			||||||
 | 
					            self.fields["select"].choices = self.choices
 | 
				
			||||||
							
								
								
									
										67
									
								
								kspneo/views.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								kspneo/views.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,67 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Author: Michel Peltriaux
 | 
				
			||||||
 | 
					Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
				
			||||||
 | 
					Contact: michel.peltriaux@sgdnord.rlp.de
 | 
				
			||||||
 | 
					Created on: 16.11.20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
 | 
					from django.contrib import messages
 | 
				
			||||||
 | 
					from django.contrib.auth import logout
 | 
				
			||||||
 | 
					from django.http import HttpRequest
 | 
				
			||||||
 | 
					from django.shortcuts import redirect, render
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from konova.contexts import BaseContext
 | 
				
			||||||
 | 
					from konova.forms import ChangeUserRoleForm
 | 
				
			||||||
 | 
					from konova.settings import SSO_SERVER_BASE
 | 
				
			||||||
 | 
					from konova.utils.session import get_session_user_role
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def logout_view(request: HttpRequest):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Logout route for ending the session manually.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Args:
 | 
				
			||||||
 | 
					        request (HttpRequest): The used request object
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Returns:
 | 
				
			||||||
 | 
					        A redirect
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    logout(request)
 | 
				
			||||||
 | 
					    return redirect(SSO_SERVER_BASE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def home_view(request: HttpRequest):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Renders the landing page
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Args:
 | 
				
			||||||
 | 
					        request (HttpRequest): The used request object
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Returns:
 | 
				
			||||||
 | 
					        A redirect
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    template = "konova/home.html"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if request.method == "POST":
 | 
				
			||||||
 | 
					        form = ChangeUserRoleForm(
 | 
				
			||||||
 | 
					            request.POST or None,
 | 
				
			||||||
 | 
					            user=request.user,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        if form.is_valid():
 | 
				
			||||||
 | 
					            role = form.save(request)
 | 
				
			||||||
 | 
					            messages.success(request, _("Role changed"))
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            messages.error(request, _("Invalid role"))
 | 
				
			||||||
 | 
					        return redirect("home")
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        # GET
 | 
				
			||||||
 | 
					        form = ChangeUserRoleForm(
 | 
				
			||||||
 | 
					            user=request.user,
 | 
				
			||||||
 | 
					            initial={"role": int(get_session_user_role(request).get("id", -1))},
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    additional_context = {
 | 
				
			||||||
 | 
					        "form": form,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    context = BaseContext(request, additional_context).context
 | 
				
			||||||
 | 
					    return render(request, template, context)
 | 
				
			||||||
							
								
								
									
										16
									
								
								kspneo/wsgi.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								kspneo/wsgi.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					WSGI config for konova project.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					It exposes the WSGI callable as a module-level variable named ``application``.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For more information on this file, see
 | 
				
			||||||
 | 
					https://docs.djangoproject.com/en/3.1/howto/deployment/wsgi/
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.core.wsgi import get_wsgi_application
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'konova.settings')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					application = get_wsgi_application()
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								locale/de/LC_MESSAGES/django.mo
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								locale/de/LC_MESSAGES/django.mo
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										1723
									
								
								locale/de/LC_MESSAGES/django.po
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1723
									
								
								locale/de/LC_MESSAGES/django.po
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										0
									
								
								logs/error.log
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								logs/error.log
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										22
									
								
								manage.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										22
									
								
								manage.py
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env python
 | 
				
			||||||
 | 
					"""Django's command-line utility for administrative tasks."""
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def main():
 | 
				
			||||||
 | 
					    """Run administrative tasks."""
 | 
				
			||||||
 | 
					    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'konova.settings')
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        from django.core.management import execute_from_command_line
 | 
				
			||||||
 | 
					    except ImportError as exc:
 | 
				
			||||||
 | 
					        raise ImportError(
 | 
				
			||||||
 | 
					            "Couldn't import Django. Are you sure it's installed and "
 | 
				
			||||||
 | 
					            "available on your PYTHONPATH environment variable? Did you "
 | 
				
			||||||
 | 
					            "forget to activate a virtual environment?"
 | 
				
			||||||
 | 
					        ) from exc
 | 
				
			||||||
 | 
					    execute_from_command_line(sys.argv)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == '__main__':
 | 
				
			||||||
 | 
					    main()
 | 
				
			||||||
							
								
								
									
										0
									
								
								organisation/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								organisation/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										15
									
								
								organisation/admin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								organisation/admin.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					from django.contrib import admin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from organisation.models import Organisation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class OrganisationAdmin(admin.ModelAdmin):
 | 
				
			||||||
 | 
					    list_display = [
 | 
				
			||||||
 | 
					        "name",
 | 
				
			||||||
 | 
					        "type",
 | 
				
			||||||
 | 
					        "created_on",
 | 
				
			||||||
 | 
					        "created_by",
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					admin.site.register(Organisation, OrganisationAdmin)
 | 
				
			||||||
							
								
								
									
										5
									
								
								organisation/apps.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								organisation/apps.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					from django.apps import AppConfig
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class OrganisationConfig(AppConfig):
 | 
				
			||||||
 | 
					    name = 'organisation'
 | 
				
			||||||
							
								
								
									
										26
									
								
								organisation/enums.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								organisation/enums.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Author: Michel Peltriaux
 | 
				
			||||||
 | 
					Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
				
			||||||
 | 
					Contact: michel.peltriaux@sgdnord.rlp.de
 | 
				
			||||||
 | 
					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"
 | 
				
			||||||
							
								
								
									
										18
									
								
								organisation/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								organisation/models.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					from django.db import models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from konova.models import BaseResource
 | 
				
			||||||
 | 
					from organisation.enums import OrganisationTypeEnum
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Organisation(BaseResource):
 | 
				
			||||||
 | 
					    name = models.CharField(max_length=500, unique=True)
 | 
				
			||||||
 | 
					    address = models.CharField(max_length=500, null=True, blank=True)
 | 
				
			||||||
 | 
					    city = models.CharField(max_length=500, null=True, blank=True)
 | 
				
			||||||
 | 
					    postal_code = models.CharField(max_length=100, null=True, blank=True)
 | 
				
			||||||
 | 
					    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
 | 
				
			||||||
							
								
								
									
										22
									
								
								organisation/settings.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								organisation/settings.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Author: Michel Peltriaux
 | 
				
			||||||
 | 
					Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
				
			||||||
 | 
					Contact: michel.peltriaux@sgdnord.rlp.de
 | 
				
			||||||
 | 
					Created on: 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"),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										3
									
								
								organisation/tests.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								organisation/tests.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					from django.test import TestCase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Create your tests here.
 | 
				
			||||||
							
								
								
									
										12
									
								
								organisation/urls.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								organisation/urls.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Author: Michel Peltriaux
 | 
				
			||||||
 | 
					Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
				
			||||||
 | 
					Contact: michel.peltriaux@sgdnord.rlp.de
 | 
				
			||||||
 | 
					Created on: 07.12.20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					from django.urls import path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					app_name = "organisation"
 | 
				
			||||||
 | 
					urlpatterns = [
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
							
								
								
									
										3
									
								
								organisation/views.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								organisation/views.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					from django.shortcuts import render
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Create your views here.
 | 
				
			||||||
							
								
								
									
										0
									
								
								process/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								process/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										38
									
								
								process/admin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								process/admin.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					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)
 | 
				
			||||||
							
								
								
									
										5
									
								
								process/apps.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								process/apps.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					from django.apps import AppConfig
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ProcessConfig(AppConfig):
 | 
				
			||||||
 | 
					    name = 'process'
 | 
				
			||||||
							
								
								
									
										202
									
								
								process/enums.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								process/enums.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,202 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					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
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										218
									
								
								process/forms.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,218 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					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
 | 
				
			||||||
							
								
								
									
										158
									
								
								process/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								process/models.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,158 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					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
 | 
				
			||||||
							
								
								
									
										19
									
								
								process/settings.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								process/settings.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					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"),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										130
									
								
								process/tables.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								process/tables.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,130 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					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)
 | 
				
			||||||
							
								
								
									
										95
									
								
								process/templates/process/open.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								process/templates/process/open.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,95 @@
 | 
				
			|||||||
 | 
					{% 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 %}
 | 
				
			||||||
							
								
								
									
										3
									
								
								process/tests.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								process/tests.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					from django.test import TestCase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Create your tests here.
 | 
				
			||||||
							
								
								
									
										22
									
								
								process/urls.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								process/urls.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Author: Michel Peltriaux
 | 
				
			||||||
 | 
					Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
				
			||||||
 | 
					Contact: michel.peltriaux@sgdnord.rlp.de
 | 
				
			||||||
 | 
					Created on: 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
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								process/views.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,217 @@
 | 
				
			|||||||
 | 
					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)
 | 
				
			||||||
							
								
								
									
										20
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					asgiref==3.3.1
 | 
				
			||||||
 | 
					certifi==2020.11.8
 | 
				
			||||||
 | 
					chardet==3.0.4
 | 
				
			||||||
 | 
					Django==3.1.3
 | 
				
			||||||
 | 
					django-autocomplete-light==3.8.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
 | 
				
			||||||
 | 
					itsdangerous==1.1.0
 | 
				
			||||||
 | 
					pkg-resources==0.0.0
 | 
				
			||||||
 | 
					psycopg2==2.8.6
 | 
				
			||||||
 | 
					pytz==2020.4
 | 
				
			||||||
 | 
					requests==2.25.0
 | 
				
			||||||
 | 
					six==1.15.0
 | 
				
			||||||
 | 
					sqlparse==0.4.1
 | 
				
			||||||
 | 
					urllib3==1.26.2
 | 
				
			||||||
 | 
					webservices==0.7
 | 
				
			||||||
							
								
								
									
										22
									
								
								templates/anonymous-user-navbar.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								templates/anonymous-user-navbar.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					{% load i18n %}
 | 
				
			||||||
 | 
					    <div class="row">
 | 
				
			||||||
 | 
					        <div class="small-12 columns menu-container">
 | 
				
			||||||
 | 
					            <div class="mobile-menu">
 | 
				
			||||||
 | 
					                <ul>
 | 
				
			||||||
 | 
					                    <li class="menu-trigger">
 | 
				
			||||||
 | 
					                        <button>{% trans 'Menu' %}</button>
 | 
				
			||||||
 | 
					                    </li>
 | 
				
			||||||
 | 
					                </ul>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <ul class="dkd_mm_section_list" data-level="1">
 | 
				
			||||||
 | 
					                <li class="dkd_mm_entry">
 | 
				
			||||||
 | 
					                    <span class="dkd_mm_link" role=button>
 | 
				
			||||||
 | 
					                        <a href="{% url 'home' %}" target="_self" title="{% trans 'Home' %}">
 | 
				
			||||||
 | 
					                            {% trans 'Home' %}
 | 
				
			||||||
 | 
					                        </a>
 | 
				
			||||||
 | 
					                    </span>
 | 
				
			||||||
 | 
					                </li>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            </ul>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
							
								
								
									
										128
									
								
								templates/authenticated-user-navbar.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								templates/authenticated-user-navbar.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,128 @@
 | 
				
			|||||||
 | 
					{% load i18n %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <div class="row">
 | 
				
			||||||
 | 
					        <div class="small-12 columns menu-container">
 | 
				
			||||||
 | 
					            <div class="mobile-menu">
 | 
				
			||||||
 | 
					                <ul>
 | 
				
			||||||
 | 
					                    <li class="menu-trigger">
 | 
				
			||||||
 | 
					                        <button>{% trans 'Menu' %}</button>
 | 
				
			||||||
 | 
					                    </li>
 | 
				
			||||||
 | 
					                </ul>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <ul class="dkd_mm_section_list" data-level="1">
 | 
				
			||||||
 | 
					                <li class="dkd_mm_entry">
 | 
				
			||||||
 | 
					                        <span class="dkd_mm_link" role=button><a href="{% url 'home' %}" target="_self">{% trans 'Home' %}</a></span>
 | 
				
			||||||
 | 
					                </li>
 | 
				
			||||||
 | 
					                <li class="dkd_mm_entry dkd_mm_sub_link">
 | 
				
			||||||
 | 
					                    <span class="dkd_mm_link" role=button><a href="{% url 'process:index' %}" target="_self">{% trans 'Process' %}</a></span>
 | 
				
			||||||
 | 
					                    <ul class="dkd_mm_section_list" data-level="2">
 | 
				
			||||||
 | 
					                        <li class="dkd_mm_section_title">
 | 
				
			||||||
 | 
					                            <span class="dkd_mm_section_title_link">{% trans 'Process management' %}</span>
 | 
				
			||||||
 | 
					                        </li>
 | 
				
			||||||
 | 
					                        <li class="dkd_mm_entry">
 | 
				
			||||||
 | 
					                            <span class="dkd_mm_link" role=button><a href="{% url 'process:index' %}" target="_self">{% trans 'Show process' %}</a></span>
 | 
				
			||||||
 | 
					                        </li>
 | 
				
			||||||
 | 
					                        <li class="dkd_mm_entry">
 | 
				
			||||||
 | 
					                            <span class="dkd_mm_link" role=button><a href="{% url 'process:new' %}" target="_self">{% trans 'New process' %}</a></span>
 | 
				
			||||||
 | 
					                        </li>
 | 
				
			||||||
 | 
					                    </ul>
 | 
				
			||||||
 | 
					                </li>
 | 
				
			||||||
 | 
					                <li class="dkd_mm_entry dkd_mm_sub_link">
 | 
				
			||||||
 | 
					                    <span class="dkd_mm_link" role=button><a href="{% url 'intervention:index' %}" target="_self">{% trans 'Intervention' %}</a></span>
 | 
				
			||||||
 | 
					                    <ul class="dkd_mm_section_list" data-level="2">
 | 
				
			||||||
 | 
					                        <li class="dkd_mm_section_title">
 | 
				
			||||||
 | 
					                            <span class="dkd_mm_section_title_link">{% trans 'Intervention management' %}</span>
 | 
				
			||||||
 | 
					                        </li>
 | 
				
			||||||
 | 
					                        <li class="dkd_mm_entry">
 | 
				
			||||||
 | 
					                            <span class="dkd_mm_link" role=button><a href="{% url 'intervention:index' %}" target="_self">{% trans 'Show intervention' %}</a></span>
 | 
				
			||||||
 | 
					                        </li>
 | 
				
			||||||
 | 
					                        <li class="dkd_mm_entry">
 | 
				
			||||||
 | 
					                            <span class="dkd_mm_link" role=button><a href="{% url 'intervention:new' %}" target="_self">{% trans 'New intervention' %}</a></span>
 | 
				
			||||||
 | 
					                        </li>
 | 
				
			||||||
 | 
					                    </ul>
 | 
				
			||||||
 | 
					                </li>
 | 
				
			||||||
 | 
					                <li class="dkd_mm_entry dkd_mm_sub_link">
 | 
				
			||||||
 | 
					                    <span class="dkd_mm_link" role=button><a href="{% url 'compensation:index' %}" target="_self">{% trans 'Compensation' %}</a></span>
 | 
				
			||||||
 | 
					                    <ul class="dkd_mm_section_list" data-level="2">
 | 
				
			||||||
 | 
					                        <li class="dkd_mm_section_title">
 | 
				
			||||||
 | 
					                            <span class="dkd_mm_section_title_link">{% trans 'Compensation management' %}</span>
 | 
				
			||||||
 | 
					                        </li>
 | 
				
			||||||
 | 
					                        <li class="dkd_mm_entry">
 | 
				
			||||||
 | 
					                            <span class="dkd_mm_link" role=button><a href="{% url 'compensation:index' %}" target="_self">{% trans 'Show compensation' %}</a></span>
 | 
				
			||||||
 | 
					                        </li>
 | 
				
			||||||
 | 
					                        <li class="dkd_mm_entry">
 | 
				
			||||||
 | 
					                            <span class="dkd_mm_link" role=button><a href="{% url 'compensation:new' %}" target="_self">{% trans 'New compensation' %}</a></span>
 | 
				
			||||||
 | 
					                        </li>
 | 
				
			||||||
 | 
					                    </ul>
 | 
				
			||||||
 | 
					                </li>
 | 
				
			||||||
 | 
					                <li class="dkd_mm_entry dkd_mm_sub_link">
 | 
				
			||||||
 | 
					                    <span class="dkd_mm_link" role=button><a href="{% url 'compensation:account-index' %}" target="_self">{% trans 'Eco-account' %}</a></span>
 | 
				
			||||||
 | 
					                    <ul class="dkd_mm_section_list" data-level="2">
 | 
				
			||||||
 | 
					                        <li class="dkd_mm_section_title">
 | 
				
			||||||
 | 
					                            <span class="dkd_mm_section_title_link">{% trans 'Eco-account management' %}</span>
 | 
				
			||||||
 | 
					                        </li>
 | 
				
			||||||
 | 
					                        <li class="dkd_mm_entry">
 | 
				
			||||||
 | 
					                            <span class="dkd_mm_link" role=button><a href="{% url 'compensation:account-index' %}" target="_self">{% trans 'Show eco-accounts' %}</a></span>
 | 
				
			||||||
 | 
					                        </li>
 | 
				
			||||||
 | 
					                        <li class="dkd_mm_entry">
 | 
				
			||||||
 | 
					                            <span class="dkd_mm_link" role=button><a href="{% url 'compensation:account-new' %}" target="_self">{% trans 'New eco-account' %}</a></span>
 | 
				
			||||||
 | 
					                        </li>
 | 
				
			||||||
 | 
					                        <li class="dkd_mm_entry">
 | 
				
			||||||
 | 
					                            <span class="dkd_mm_link" role=button><a href="{% url 'compensation:account-index' %}" target="_self">{% trans 'Withdraw from eco-account' %}</a></span>
 | 
				
			||||||
 | 
					                        </li>
 | 
				
			||||||
 | 
					                    </ul>
 | 
				
			||||||
 | 
					                </li>
 | 
				
			||||||
 | 
					                <li class="dkd_mm_entry dkd_mm_sub_link">
 | 
				
			||||||
 | 
					                    <span class="dkd_mm_link" role=button><a href="{% url 'compensation:index' %}" target="_self">EMA Alte Rechtslage</a></span>
 | 
				
			||||||
 | 
					                    <ul class="dkd_mm_section_list" data-level="2">
 | 
				
			||||||
 | 
					                        <li class="dkd_mm_section_title">
 | 
				
			||||||
 | 
					                            <span class="dkd_mm_section_title_link">Maßnahmen aus Ersatzzahlungen - alte Rechtslage (EMA)</span>
 | 
				
			||||||
 | 
					                        </li>
 | 
				
			||||||
 | 
					                        <li class="dkd_mm_entry">
 | 
				
			||||||
 | 
					                            <span class="dkd_mm_link" role=button><a href="{% url 'compensation:index' %}" target="_self">{% trans 'Show actions' %}</a></span>
 | 
				
			||||||
 | 
					                        </li>
 | 
				
			||||||
 | 
					                        <li class="dkd_mm_entry">
 | 
				
			||||||
 | 
					                            <span class="dkd_mm_link" role=button><a href="{% url 'compensation:new' %}" target="_self">{% trans 'New action' %}</a></span>
 | 
				
			||||||
 | 
					                        </li>
 | 
				
			||||||
 | 
					                    </ul>
 | 
				
			||||||
 | 
					                </li>
 | 
				
			||||||
 | 
					                <li class="dkd_mm_entry dkd_mm_sub_link">
 | 
				
			||||||
 | 
					                    <span class="dkd_mm_link" role=button><a href="{% url 'compensation:index' %}" target="_self">{% trans 'Organization' %}</a></span>
 | 
				
			||||||
 | 
					                    <ul class="dkd_mm_section_list" data-level="2">
 | 
				
			||||||
 | 
					                        <li class="dkd_mm_section_title">
 | 
				
			||||||
 | 
					                            <span class="dkd_mm_section_title_link">{% trans 'Organization' %}</span>
 | 
				
			||||||
 | 
					                        </li>
 | 
				
			||||||
 | 
					                        <li class="dkd_mm_entry">
 | 
				
			||||||
 | 
					                            <span class="dkd_mm_link" role=button><a href="" target="_self">{% trans 'Import / Export' %}</a></span>
 | 
				
			||||||
 | 
					                        </li>
 | 
				
			||||||
 | 
					                        <li class="dkd_mm_entry">
 | 
				
			||||||
 | 
					                            <span class="dkd_mm_link" role=button><a href="" target="_self">{% trans 'Annual report' %}</a></span>
 | 
				
			||||||
 | 
					                        </li>
 | 
				
			||||||
 | 
					                        <li class="dkd_mm_entry">
 | 
				
			||||||
 | 
					                            <span class="dkd_mm_link" role=button><a href="" target="_self">{% trans 'Settings' %}</a></span>
 | 
				
			||||||
 | 
					                        </li>
 | 
				
			||||||
 | 
					                        <li class="dkd_mm_entry">
 | 
				
			||||||
 | 
					                            <span class="dkd_mm_link" role=button><a href="{{ wiki_url }}" target="_blank">{% trans 'Help' %}</a></span>
 | 
				
			||||||
 | 
					                        </li>
 | 
				
			||||||
 | 
					                    </ul>
 | 
				
			||||||
 | 
					                </li>
 | 
				
			||||||
 | 
					                <li class="dkd_mm_entry dkd_mm_sub_link">
 | 
				
			||||||
 | 
					                    <span class="dkd_mm_link" role=button><a href="{% url 'compensation:index' %}" target="_self">{% trans 'User' %}: {{ user }}</a></span>
 | 
				
			||||||
 | 
					                    <ul class="dkd_mm_section_list" data-level="2">
 | 
				
			||||||
 | 
					                        <li class="dkd_mm_section_title">
 | 
				
			||||||
 | 
					                            <span class="dkd_mm_section_title_link">{% trans 'User' %}: {{ user }}</span>
 | 
				
			||||||
 | 
					                        </li>
 | 
				
			||||||
 | 
					                        <li class="dkd_mm_entry">
 | 
				
			||||||
 | 
					                            <span class="dkd_mm_link" role=button><a href="{% url 'logout' %}" target="_self">{% trans 'Logout' %}</a></span>
 | 
				
			||||||
 | 
					                        </li>
 | 
				
			||||||
 | 
					                    </ul>
 | 
				
			||||||
 | 
					                </li>
 | 
				
			||||||
 | 
					            </ul>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <div class="user-role">
 | 
				
			||||||
 | 
					            {% trans 'You are currently working as ' %}
 | 
				
			||||||
 | 
					            <strong>{{ current_role.type }} ({{ current_role.org }})</strong>
 | 
				
			||||||
 | 
					            <a href="{% url 'home' %}">{% trans 'Change...' %}</a>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
							
								
								
									
										100
									
								
								templates/base.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								templates/base.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,100 @@
 | 
				
			|||||||
 | 
					<!DOCTYPE html>
 | 
				
			||||||
 | 
					{% load static i18n fontawesome_5 %}
 | 
				
			||||||
 | 
					<html lang="{{ language }}">
 | 
				
			||||||
 | 
					<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>
 | 
				
			||||||
 | 
					    {% fontawesome_5_static %}
 | 
				
			||||||
 | 
					    {% block head %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    {% endblock %}
 | 
				
			||||||
 | 
					</head>
 | 
				
			||||||
 | 
					<body>
 | 
				
			||||||
 | 
					{% if render_header %}
 | 
				
			||||||
 | 
					<header class="header">
 | 
				
			||||||
 | 
					    <div class="background-wrap gray-600">
 | 
				
			||||||
 | 
					        <div class="row logo-search-area">
 | 
				
			||||||
 | 
									<div class="small-6 medium-4 columns">
 | 
				
			||||||
 | 
					                    <h3>{{ base_frontend_title }}</h3>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
									<div class="medium-4 columns hide-for-small-only"></div>
 | 
				
			||||||
 | 
									<div class="small-6 medium-4 columns last">
 | 
				
			||||||
 | 
										<a href="{% url 'home' %}">
 | 
				
			||||||
 | 
					                        <img class="logo" alt="{% trans 'Home' %}"
 | 
				
			||||||
 | 
					                            title="{% trans 'Home' %}"
 | 
				
			||||||
 | 
					                             src="{% static 'images/rlp-logos-MUEEF.png' %}"/>
 | 
				
			||||||
 | 
					                        <noscript>
 | 
				
			||||||
 | 
					                            <img title="rlp" alt="rlp-logo" src="{% static 'images/rlp-logos-MUEEF.png' %}" width="192" height="84" />
 | 
				
			||||||
 | 
					                        </noscript>
 | 
				
			||||||
 | 
					                    </a>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</header>
 | 
				
			||||||
 | 
					{% endif %}
 | 
				
			||||||
 | 
					    <div class="body-content">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <nav class="main-menu static" role="navigation">
 | 
				
			||||||
 | 
					            {% block navbar %}
 | 
				
			||||||
 | 
					            {% if user.is_authenticated %}
 | 
				
			||||||
 | 
					                {% include 'authenticated-user-navbar.html' %}
 | 
				
			||||||
 | 
					            {% else %}
 | 
				
			||||||
 | 
					                {% include 'anonymous-user-navbar.html' %}
 | 
				
			||||||
 | 
					            {% endif %}
 | 
				
			||||||
 | 
					            {% endblock %}
 | 
				
			||||||
 | 
					        </nav>
 | 
				
			||||||
 | 
					        <div class="row">
 | 
				
			||||||
 | 
					            <div class="large-12 columns">
 | 
				
			||||||
 | 
					            {% for message in messages %}
 | 
				
			||||||
 | 
					                <div class="{{ message.tags }}">
 | 
				
			||||||
 | 
					                    {{ message }}
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            {% endfor %}
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <div class="small-12 columns">
 | 
				
			||||||
 | 
					                <div class="column" style="margin-top: 50px;">
 | 
				
			||||||
 | 
					                    {% block body %}
 | 
				
			||||||
 | 
					                        <div class="small-12 medium-3 columns">
 | 
				
			||||||
 | 
					                            {% block body_left %}
 | 
				
			||||||
 | 
					                             
 | 
				
			||||||
 | 
					                            {% endblock %}
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                        <div class="small-12 medium-6 columns">
 | 
				
			||||||
 | 
					                            {% block body_middle %}
 | 
				
			||||||
 | 
					                            {% endblock %}
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                        <div class="small-12 medium-3 columns">
 | 
				
			||||||
 | 
					                            {% block body_right %}
 | 
				
			||||||
 | 
					                             
 | 
				
			||||||
 | 
					                            {% endblock %}
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                    {% endblock %}
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <footer class="footer">
 | 
				
			||||||
 | 
					        {% block footer %}
 | 
				
			||||||
 | 
					        <div class="background-wrap gray-500 line"></div>
 | 
				
			||||||
 | 
					        <div class="row">
 | 
				
			||||||
 | 
					            <div class="large-6 medium-6 columns">
 | 
				
			||||||
 | 
					                <h2 class="h6">{% trans 'About this site' %}</h2>
 | 
				
			||||||
 | 
					                <ul class="no-bullet">
 | 
				
			||||||
 | 
					                    <li><a href="" target="_self">Impressum</a></li>
 | 
				
			||||||
 | 
					                    <li><a href="" target="_self">{% trans 'Privacy policy' %}</a></li>
 | 
				
			||||||
 | 
					                </ul>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        {% endblock %}
 | 
				
			||||||
 | 
					    </footer>
 | 
				
			||||||
 | 
					</body>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</html>
 | 
				
			||||||
							
								
								
									
										7
									
								
								templates/generic_index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								templates/generic_index.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					{% extends 'base.html' %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block body %}
 | 
				
			||||||
 | 
					    <div class="style-2">
 | 
				
			||||||
 | 
					    {% include 'table.html' %}
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
							
								
								
									
										54
									
								
								templates/generic_table_form.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								templates/generic_table_form.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,54 @@
 | 
				
			|||||||
 | 
					{% load i18n  %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block head %}
 | 
				
			||||||
 | 
					    {{ form.media }}
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block body %}
 | 
				
			||||||
 | 
					<div class="table-container">
 | 
				
			||||||
 | 
					    <h4>
 | 
				
			||||||
 | 
					        {{ form.form_title }}
 | 
				
			||||||
 | 
					    </h4>
 | 
				
			||||||
 | 
					    {% if form.form_caption is not None %}
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					        {{ form.form_caption }}
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    {% endif %}
 | 
				
			||||||
 | 
					    <form method="post" action="{{ form.action_url }}">
 | 
				
			||||||
 | 
					        {% csrf_token %}
 | 
				
			||||||
 | 
					        <table>
 | 
				
			||||||
 | 
					            <tbody>
 | 
				
			||||||
 | 
					                {% for field in form %}
 | 
				
			||||||
 | 
					                <tr title="{{ field.help_text }}" class="{% if field.errors %}error{% endif %}">
 | 
				
			||||||
 | 
					                    <th scope="row" class="small-3">
 | 
				
			||||||
 | 
					                        <div>{{ field.label }}<span class="label-required">{% if field.field.required %}*{% endif %}</span></div>
 | 
				
			||||||
 | 
					                        <small>{{ field.help_text }}</small>
 | 
				
			||||||
 | 
					                    </th>
 | 
				
			||||||
 | 
					                    <td class="small-12">
 | 
				
			||||||
 | 
					                        {{ field }}
 | 
				
			||||||
 | 
					                        {% for error in field.errors %}
 | 
				
			||||||
 | 
					                        <b>{{ error }}</b>
 | 
				
			||||||
 | 
					                        {% endfor %}
 | 
				
			||||||
 | 
					                    </td>
 | 
				
			||||||
 | 
					                </tr>
 | 
				
			||||||
 | 
					                {% endfor %}
 | 
				
			||||||
 | 
					            </tbody>
 | 
				
			||||||
 | 
					        </table>
 | 
				
			||||||
 | 
					        <div class="label-required">{% trans 'Fields with * are required.' %}</div>
 | 
				
			||||||
 | 
					        <div class="row">
 | 
				
			||||||
 | 
					            <div class="large-1 columns">
 | 
				
			||||||
 | 
					                <a href="{{ form.cancel_redirect }}">
 | 
				
			||||||
 | 
					                    <button class="button small append-value" type="button" title="{% trans 'Cancel' %}">{% trans 'Cancel' %}</button>
 | 
				
			||||||
 | 
					                </a>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="large-10 columns">
 | 
				
			||||||
 | 
					                <button class="button small append-value right" type="submit" title="{% trans 'Save' %}">{% trans 'Save' %}</button>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </form>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block footer %}
 | 
				
			||||||
 | 
					    {{ form.media }}
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
							
								
								
									
										44
									
								
								templates/table.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								templates/table.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					{% load django_tables2 %}
 | 
				
			||||||
 | 
					{% load i18n static %}
 | 
				
			||||||
 | 
					    <div class="rows">
 | 
				
			||||||
 | 
					        {% if table.title is not None %}
 | 
				
			||||||
 | 
					            <div class="rows">
 | 
				
			||||||
 | 
					                <h3>
 | 
				
			||||||
 | 
					                    {{ table.title }}
 | 
				
			||||||
 | 
					                </h3>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        {% endif %}
 | 
				
			||||||
 | 
					        <div class="column large-2">
 | 
				
			||||||
 | 
					            {% if table.user.is_superuser and table.add_new_entries %}
 | 
				
			||||||
 | 
					            <a href="{{ table.add_new_url }}">
 | 
				
			||||||
 | 
					                <button class="button small" title="{% trans 'New entry' %}">
 | 
				
			||||||
 | 
					                    <i class="fas fa-plus"></i>
 | 
				
			||||||
 | 
					                    {% trans 'New' %}
 | 
				
			||||||
 | 
					                </button>
 | 
				
			||||||
 | 
					            </a>
 | 
				
			||||||
 | 
					            {% else %}
 | 
				
			||||||
 | 
					                 
 | 
				
			||||||
 | 
					            {% endif %}
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="column large-8">
 | 
				
			||||||
 | 
					                 
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="column large-2 dropdown-area">
 | 
				
			||||||
 | 
					            <form method="get">
 | 
				
			||||||
 | 
					                {{ table.filter.form.as_p }}
 | 
				
			||||||
 | 
					            </form>
 | 
				
			||||||
 | 
					            <div class="header-meta-dropdown">
 | 
				
			||||||
 | 
					                <button data-dropdown="rpp-choice" aria-controls="rpp-choice" aria-expanded="false" class="custom-dropdown left">{% trans 'Results per page' %}</button>
 | 
				
			||||||
 | 
					                <ul id="rpp-choice" class="custom-dropdown-content" data-dropdown-content aria-hidden="true" style="position: absolute; left: -99999px; top: 25px; right: auto;">
 | 
				
			||||||
 | 
					                {% for rpp_option in table.results_per_page_choices %}
 | 
				
			||||||
 | 
					                    <li class="{% if table.results_per_page_chosen == rpp_option %}selected{% endif %}">
 | 
				
			||||||
 | 
					                        <a class="" href="{% querystring table.results_per_page_parameter=rpp_option %}">{{ rpp_option }}</a>
 | 
				
			||||||
 | 
					                    </li>
 | 
				
			||||||
 | 
					                {% endfor %}
 | 
				
			||||||
 | 
					                </ul>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div class="row">
 | 
				
			||||||
 | 
					        {% render_table table %}
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user