diff --git a/compensation/admin.py b/compensation/admin.py index 72888d4c..104048d8 100644 --- a/compensation/admin.py +++ b/compensation/admin.py @@ -17,8 +17,7 @@ class CompensationStateAdmin(admin.ModelAdmin): list_display = [ "id", "biotope_type", - "amount", - "unit", + "surface", ] @@ -35,7 +34,6 @@ class CompensationActionAdmin(admin.ModelAdmin): class CompensationAdmin(admin.ModelAdmin): list_display = [ "id", - "type", "created_on", ] diff --git a/compensation/models.py b/compensation/models.py index 19e6ad0d..9d3e0ca5 100644 --- a/compensation/models.py +++ b/compensation/models.py @@ -6,6 +6,8 @@ Created on: 17.11.20 """ from django.contrib.gis.db import models +from django.core.validators import MinValueValidator +from django.utils import timezone from django.utils.timezone import now from compensation.settings import COMPENSATION_IDENTIFIER_LENGTH, COMPENSATION_IDENTIFIER_TEMPLATE @@ -14,6 +16,14 @@ from konova.utils.generators import generate_random_string from organisation.models import Organisation +class Payment(BaseResource): + """ + Holds data on a payment for an intervention (alternative to a classic compensation) + """ + amount = models.FloatField(validators=[MinValueValidator(limit_value=0.00)]) + due_on = models.DateField(null=True) + + class CompensationControl(BaseResource): """ Holds data on how a compensation shall be controlled @@ -48,8 +58,8 @@ 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. """ - registration_office = models.ForeignKey(Organisation, on_delete=models.SET_NULL, null=True) - conservation_office = models.ForeignKey(Organisation, on_delete=models.SET_NULL, null=True) + registration_office = models.ForeignKey(Organisation, on_delete=models.SET_NULL, null=True, related_name="+") + conservation_office = models.ForeignKey(Organisation, on_delete=models.SET_NULL, null=True, related_name="+") 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! @@ -58,14 +68,14 @@ class Compensation(BaseObject): after_states = models.ManyToManyField(CompensationState, blank=True, related_name='+') actions = models.ManyToManyField(CompensationAction) - deadline_creation = models.ForeignKey("konova.Deadline", on_delete=models.SET_NULL, null=True, blank=True, related_name="deadline_creation") - deadline_maintaining = models.ForeignKey("konova.Deadline", on_delete=models.SET_NULL, null=True, blank=True, related_name="deadline_maintaining") + deadline_creation = models.ForeignKey("konova.Deadline", on_delete=models.SET_NULL, null=True, blank=True, related_name="+") + deadline_maintaining = models.ForeignKey("konova.Deadline", on_delete=models.SET_NULL, null=True, blank=True, related_name="+") geometry = models.ForeignKey(Geometry, null=True, blank=True, on_delete=models.SET_NULL) documents = models.ManyToManyField("konova.Document", blank=True) @staticmethod - def __generate_new_identifier() -> str: + def _generate_new_identifier() -> str: """ Generates a new identifier for the intervention object Returns: @@ -80,12 +90,30 @@ class Compensation(BaseObject): _str = "{}{}{}".format(curr_month, curr_year, rand_str) return COMPENSATION_IDENTIFIER_TEMPLATE.format(_str) + def delete(self, *args, **kwargs): + """ Custom delete functionality + + Does not delete from database but sets a timestamp for being deleted on and which user deleted the object + + Args: + *args (): + **kwargs (): + + Returns: + + """ + _now = timezone.now() + _user = kwargs.get("user", None) + self.deleted_on = _now + self.deleted_by = _user + self.save() + def save(self, *args, **kwargs): if self.identifier is None or len(self.identifier) == 0: # Create new identifier - new_id = self.__generate_new_identifier() + new_id = self._generate_new_identifier() while Compensation.objects.filter(identifier=new_id).exists(): - new_id = self.__generate_new_identifier() + new_id = self._generate_new_identifier() self.identifier = new_id super().save(*args, **kwargs) @@ -94,8 +122,5 @@ class EcoAccount(Compensation): """ An eco account is a kind of 'prepaid' compensation. It can be compared to an account that already has been filled with some kind of currency. From this account one is able to 'withdraw' currency for current projects. - - 'Withdrawing' can only be applied by shrinking the size of the available geometry and declaring the withdrawed - geometry as a compensation for a process. """ handler = models.CharField(max_length=500, null=True, blank=True, help_text="Who is responsible for handling the actions") diff --git a/compensation/tables.py b/compensation/tables.py index b4795dd6..8f22bbc2 100644 --- a/compensation/tables.py +++ b/compensation/tables.py @@ -24,11 +24,6 @@ class CompensationTable(BaseTable): orderable=True, accessor="title", ) - p = tables.Column( - verbose_name=_("Process"), - orderable=True, - accessor="process", - ) d = tables.Column( verbose_name=_("Created on"), orderable=True, diff --git a/intervention/admin.py b/intervention/admin.py index 18fcf743..6d0df042 100644 --- a/intervention/admin.py +++ b/intervention/admin.py @@ -9,7 +9,6 @@ class InterventionAdmin(admin.ModelAdmin): "title", "process_type", "handler", - "is_active", "created_on", "deleted_on", ] diff --git a/intervention/models.py b/intervention/models.py index 9096f173..ee796518 100644 --- a/intervention/models.py +++ b/intervention/models.py @@ -7,8 +7,11 @@ Created on: 17.11.20 """ from django.contrib.auth.models import User from django.contrib.gis.db import models +from django.db import transaction +from django.utils import timezone from django.utils.timezone import now +from compensation.models import Payment, Compensation from intervention.settings import INTERVENTION_IDENTIFIER_LENGTH, INTERVENTION_IDENTIFIER_TEMPLATE from konova.models import BaseObject, Geometry from konova.utils.generators import generate_random_string @@ -18,22 +21,23 @@ from organisation.models import Organisation 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 """ - registration_office = models.ForeignKey(Organisation, on_delete=models.SET_NULL, null=True) + registration_office = models.ForeignKey(Organisation, on_delete=models.SET_NULL, null=True, related_name="+") registration_file_number = models.CharField(max_length=1000, blank=True, null=True) - registration_date = models.DateTimeField(null=True, blank=True) - - conservation_office = models.ForeignKey(Organisation, on_delete=models.SET_NULL, null=True) + conservation_office = models.ForeignKey(Organisation, on_delete=models.SET_NULL, null=True, related_name="+") conservations_file_number = models.CharField(max_length=1000, blank=True, null=True) - process_type = models.CharField(max_length=500, null=True, blank=True) law = models.CharField(max_length=500, null=True, blank=True) handler = models.CharField(max_length=500, null=True, blank=True) geometry = models.ForeignKey(Geometry, null=True, blank=True, on_delete=models.SET_NULL) documents = models.ManyToManyField("konova.Document", blank=True) + # Refers to "zugelassen am" + registration_date = models.DateField(null=True, blank=True) + + # Refers to "Bestandskraft am" + binding_on = models.DateField(null=True, blank=True) + # Refers to "verzeichnen" recorded_on = models.DateTimeField(default=None) recorded_by = models.ForeignKey(User, null=True, blank=True, on_delete=models.SET_NULL) @@ -41,14 +45,39 @@ class Intervention(BaseObject): # Holds which intervention is simply a newer version of this dataset next_version = models.ForeignKey("Intervention", null=True, on_delete=models.DO_NOTHING) - def __str__(self): - return "{} by {}".format(self.type, self.handler) + # Compensation or payments, one-directional + payments = models.ManyToManyField(Payment, related_name="+") + compensations = models.ManyToManyField(Compensation, related_name="+") def delete(self, *args, **kwargs): - super().delete(*args, **kwargs) + """ Custom delete functionality + + Does not delete from database but sets a timestamp for being deleted on and which user deleted the object + + Args: + *args (): + **kwargs (): + + Returns: + + """ + _now = timezone.now() + _user = kwargs.get("user", None) + + with transaction.atomic(): + # "Delete" related compensations as well + coms = self.compensations.all() + for com in coms: + com.deleted_on = _now + com.deleted_by = _user + com.save() + + self.deleted_on = _now + self.deleted_by = _user + self.save() @staticmethod - def __generate_new_identifier() -> str: + def _generate_new_identifier() -> str: """ Generates a new identifier for the intervention object Returns: @@ -66,8 +95,8 @@ class Intervention(BaseObject): def save(self, *args, **kwargs): if self.identifier is None or len(self.identifier) == 0: # Create new identifier - new_id = self.__generate_new_identifier() + new_id = self._generate_new_identifier() while Intervention.objects.filter(identifier=new_id).exists(): - new_id = self.__generate_new_identifier() + new_id = self._generate_new_identifier() self.identifier = new_id super().save(*args, **kwargs) \ No newline at end of file diff --git a/intervention/tables.py b/intervention/tables.py index 6f8f3431..b82e2fb0 100644 --- a/intervention/tables.py +++ b/intervention/tables.py @@ -25,11 +25,6 @@ class InterventionTable(BaseTable): orderable=True, accessor="title", ) - p = tables.Column( - verbose_name=_("Process"), - orderable=True, - accessor="process", - ) d = tables.Column( verbose_name=_("Created on"), orderable=True, diff --git a/intervention/views.py b/intervention/views.py index 4181dd05..a79c744c 100644 --- a/intervention/views.py +++ b/intervention/views.py @@ -2,8 +2,7 @@ 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 django.shortcuts import render, get_object_or_404 from intervention.forms import NewInterventionForm, EditInterventionForm, OpenInterventionForm from intervention.models import Intervention @@ -11,14 +10,12 @@ 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 - def index_view(request: HttpRequest): """ - Renders the index view for process + Renders the index view for Interventions Args: request (HttpRequest): The incoming request @@ -56,14 +53,8 @@ def new_view(request: HttpRequest): 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") + messages.success(request, _("Intervention {} added").format(intervention.title)) + return redirect("intervention:index") else: messages.error(request, _("Invalid input")) else: @@ -129,7 +120,7 @@ def edit_view(request: HttpRequest, id: str): @login_required def remove_view(request: HttpRequest, id: str): - """ Renders a remove view for this process + """ Renders a remove view for this intervention Args: request (HttpRequest): The incoming request @@ -139,29 +130,13 @@ def remove_view(request: HttpRequest, id: str): """ 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")) + obj = Intervention.objects.get(id=id) + + # ToDo form = RemoveForm( object_to_remove=obj, - remove_post_url=reverse("process:remove", args=(process.id,)), + remove_post_url=reverse("intervention:remove", args=(id,)), cancel_url=reverse("intervention:index"), ) context = { diff --git a/konova/admin.py b/konova/admin.py deleted file mode 100644 index f2f21f4a..00000000 --- a/konova/admin.py +++ /dev/null @@ -1,32 +0,0 @@ -""" -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) - - diff --git a/konova/autocompletes.py b/konova/autocompletes.py index 172b1546..21319bf2 100644 --- a/konova/autocompletes.py +++ b/konova/autocompletes.py @@ -7,7 +7,6 @@ Created on: 07.12.20 """ from dal_select2.views import Select2QuerySetView -from organisation.enums import OrganisationTypeEnum from organisation.models import Organisation @@ -33,9 +32,6 @@ class NonOfficialOrganisationAutocomplete(Select2QuerySetView): qs = qs.filter( name__icontains=self.q, ) - qs = qs.exclude( - type=OrganisationTypeEnum.OFFICIAL.value - ) qs = qs.order_by( "name" ) diff --git a/konova/forms.py b/konova/forms.py index e0e400f7..a97fd20e 100644 --- a/konova/forms.py +++ b/konova/forms.py @@ -9,14 +9,8 @@ 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): """ @@ -97,45 +91,3 @@ class RemoveForm(BaseForm): 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 \ No newline at end of file diff --git a/konova/management/commands/setup.py b/konova/management/commands/setup.py index 1e1aeec3..58e93355 100644 --- a/konova/management/commands/setup.py +++ b/konova/management/commands/setup.py @@ -11,8 +11,7 @@ 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 konova.management.commands.setup_test_data import TEST_ORGANISATION_DATA from organisation.models import Organisation CREATED_TEMPLATE = "{} created" @@ -26,7 +25,6 @@ class Command(BaseCommand): with transaction.atomic(): self.__init_superuser() self.__init_test_organisation() - self.__init_role_groups() except KeyboardInterrupt: self.__break_line() exit(-1) @@ -96,30 +94,6 @@ class Command(BaseCommand): ) 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 diff --git a/konova/models.py b/konova/models.py index 907081c5..5bf5b2a8 100644 --- a/konova/models.py +++ b/konova/models.py @@ -21,7 +21,7 @@ class BaseResource(models.Model): default=uuid.uuid4, ) created_on = models.DateTimeField(auto_now_add=True, null=True) - created_by = models.ForeignKey(User, null=True, on_delete=models.SET_NULL) + created_by = models.ForeignKey(User, null=True, on_delete=models.SET_NULL, related_name="+") class Meta: abstract = True @@ -36,7 +36,7 @@ class BaseObject(BaseResource): identifier = models.CharField(max_length=1000, null=True, blank=True) title = models.CharField(max_length=1000, null=True, blank=True) deleted_on = models.DateTimeField(null=True) - deleted_by = models.ForeignKey(User, null=True, on_delete=models.SET_NULL) + deleted_by = models.ForeignKey(User, null=True, on_delete=models.SET_NULL, related_name="+") comment = models.TextField() class Meta: @@ -56,7 +56,7 @@ class Deadline(BaseResource): class Document(BaseResource): """ - Documents can be attached to process, compensation or intervention for uploading legal documents or pictures. + Documents can be attached to compensation or intervention for uploading legal documents or pictures. """ date_of_creation = models.DateField() document = models.FileField() diff --git a/konova/sub_settings/django_settings.py b/konova/sub_settings/django_settings.py index ffc16433..3a3d7afd 100644 --- a/konova/sub_settings/django_settings.py +++ b/konova/sub_settings/django_settings.py @@ -60,7 +60,6 @@ INSTALLED_APPS = [ 'konova', 'compensation', 'intervention', - 'process', 'organisation', ] if DEBUG: diff --git a/konova/templatetags/__init__.py b/konova/templatetags/__init__.py deleted file mode 100644 index 2c37b672..00000000 --- a/konova/templatetags/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -""" -Author: Michel Peltriaux -Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany -Contact: michel.peltriaux@sgdnord.rlp.de -Created on: 04.12.20 - -""" diff --git a/konova/templatetags/custom_tags.py b/konova/templatetags/custom_tags.py deleted file mode 100644 index 1577e927..00000000 --- a/konova/templatetags/custom_tags.py +++ /dev/null @@ -1,17 +0,0 @@ -""" -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) diff --git a/konova/urls.py b/konova/urls.py index f914ccd9..42c39082 100644 --- a/konova/urls.py +++ b/konova/urls.py @@ -30,10 +30,10 @@ urlpatterns = [ path('', home_view, name="home"), path('intervention/', include("intervention.urls")), path('compensation/', include("compensation.urls")), - path('eco-account/', include("process.urls")), - path('ema/', include("process.urls")), + path('eco-account/', include("intervention.urls")), #ToDo + path('ema/', include("intervention.urls")), #ToDo path('organisation/', include("organisation.urls")), - path('user/', include("process.urls")), + path('user/', include("intervention.urls")), #ToDo # Autocomplete paths path("atcmplt/orgs", OrganisationAutocomplete.as_view(), name="orgs-autocomplete"), diff --git a/konova/utils/session.py b/konova/utils/session.py deleted file mode 100644 index 2aa475f5..00000000 --- a/konova/utils/session.py +++ /dev/null @@ -1,45 +0,0 @@ -""" -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, {}) diff --git a/konova/views.py b/konova/views.py index 34baada5..3d8e6a26 100644 --- a/konova/views.py +++ b/konova/views.py @@ -5,16 +5,12 @@ 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): diff --git a/organisation/admin.py b/organisation/admin.py index 6988d35e..8fd4a529 100644 --- a/organisation/admin.py +++ b/organisation/admin.py @@ -6,7 +6,6 @@ from organisation.models import Organisation class OrganisationAdmin(admin.ModelAdmin): list_display = [ "name", - "type", "created_on", "created_by", ]