Merge pull request 'test' (#347) from test into master
Reviewed-on: SGD-Nord/konova#347
This commit is contained in:
		
						commit
						ffae18ebc4
					
				
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -1,3 +1,5 @@
 | 
			
		||||
# Project exclude paths
 | 
			
		||||
/venv/
 | 
			
		||||
/.idea/
 | 
			
		||||
/.idea/
 | 
			
		||||
/.coverage
 | 
			
		||||
/htmlcov/
 | 
			
		||||
 | 
			
		||||
@ -9,4 +9,8 @@ Created on: 19.10.21
 | 
			
		||||
# Defines the date of the legal publishing of the LKompVzVo
 | 
			
		||||
from django.utils import timezone
 | 
			
		||||
 | 
			
		||||
LKOMPVZVO_PUBLISH_DATE = timezone.make_aware(timezone.datetime.fromisoformat("2018-06-16")).date()
 | 
			
		||||
LKOMPVZVO_PUBLISH_DATE = timezone.make_aware(
 | 
			
		||||
    timezone.datetime.fromisoformat(
 | 
			
		||||
        "2018-06-16"
 | 
			
		||||
    )
 | 
			
		||||
).date()
 | 
			
		||||
 | 
			
		||||
@ -31,6 +31,6 @@
 | 
			
		||||
    {% include 'analysis/reports/includes/intervention/card_intervention.html' %}
 | 
			
		||||
    {% include 'analysis/reports/includes/compensation/card_compensation.html' %}
 | 
			
		||||
    {% include 'analysis/reports/includes/eco_account/card_eco_account.html' %}
 | 
			
		||||
    {% include 'analysis/reports/includes/old_data/card_old_interventions.html' %}
 | 
			
		||||
    {% include 'analysis/reports/includes/old_data/card_old_data.html' %}
 | 
			
		||||
    </div>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
@ -10,6 +10,7 @@
 | 
			
		||||
                                {% fa5_icon 'leaf' %}
 | 
			
		||||
                                {% trans 'Compensations' %}
 | 
			
		||||
                            </h5>
 | 
			
		||||
                            <span>{% trans 'Binding date after' %} 16.06.2018</span>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
@ -10,6 +10,7 @@
 | 
			
		||||
                                {% fa5_icon 'tree' %}
 | 
			
		||||
                                {% trans 'Eco-Accounts' %}
 | 
			
		||||
                            </h5>
 | 
			
		||||
                            <span>{% trans 'Binding date after' %} 16.06.2018</span>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,7 @@
 | 
			
		||||
                                {% fa5_icon 'pencil-ruler' %}
 | 
			
		||||
                                {% trans 'Interventions' %}
 | 
			
		||||
                            </h5>
 | 
			
		||||
                            <span>{% trans 'Binding date after' %} 16.06.2018</span>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
@ -1,3 +0,0 @@
 | 
			
		||||
from django.test import TestCase
 | 
			
		||||
 | 
			
		||||
# Create your tests here.
 | 
			
		||||
							
								
								
									
										7
									
								
								analysis/tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								analysis/tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: ksp-servicestelle@sgdnord.rlp.de
 | 
			
		||||
Created on: 15.08.23
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
							
								
								
									
										7
									
								
								analysis/tests/unit/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								analysis/tests/unit/__init__.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: ksp-servicestelle@sgdnord.rlp.de
 | 
			
		||||
Created on: 15.08.23
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
							
								
								
									
										47
									
								
								analysis/tests/unit/test_forms.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								analysis/tests/unit/test_forms.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,47 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: ksp-servicestelle@sgdnord.rlp.de
 | 
			
		||||
Created on: 15.08.23
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
from datetime import timedelta
 | 
			
		||||
 | 
			
		||||
from django.urls import reverse
 | 
			
		||||
from django.utils.timezone import now
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from analysis.forms import TimespanReportForm
 | 
			
		||||
from konova.tests.test_views import BaseTestCase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TimeSpanReportFormTestCase(BaseTestCase):
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
        eiv = self.create_dummy_intervention()
 | 
			
		||||
 | 
			
		||||
    def test_init(self):
 | 
			
		||||
        form = TimespanReportForm()
 | 
			
		||||
        self.assertEqual(form.form_title, str(_("Generate report")))
 | 
			
		||||
        self.assertEqual(form.form_caption, str(_("Select a timespan and the desired conservation office") ))
 | 
			
		||||
        self.assertEqual(form.action_url, reverse("analysis:reports"))
 | 
			
		||||
        self.assertFalse(form.show_cancel_btn)
 | 
			
		||||
        self.assertEqual(form.action_btn_label, str(_("Continue")))
 | 
			
		||||
 | 
			
		||||
    def test_save(self):
 | 
			
		||||
        date_from = now().date() - timedelta(days=365)
 | 
			
		||||
        date_to = now().date()
 | 
			
		||||
        office = self.get_conservation_office_code()
 | 
			
		||||
        data = {
 | 
			
		||||
            "date_from": date_from,
 | 
			
		||||
            "date_to": date_to,
 | 
			
		||||
            "conservation_office": office,
 | 
			
		||||
        }
 | 
			
		||||
        form = TimespanReportForm(data)
 | 
			
		||||
        self.assertTrue(form.is_valid(), msg=f"{form.errors}")
 | 
			
		||||
 | 
			
		||||
        detail_report_url = form.save()
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            detail_report_url,
 | 
			
		||||
            reverse("analysis:report-detail", args=(office.id,)) + f"?df={date_from}&dt={date_to}"
 | 
			
		||||
        )
 | 
			
		||||
							
								
								
									
										98
									
								
								analysis/tests/unit/test_report.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								analysis/tests/unit/test_report.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,98 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: ksp-servicestelle@sgdnord.rlp.de
 | 
			
		||||
Created on: 17.08.23
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
from datetime import timedelta
 | 
			
		||||
 | 
			
		||||
from django.utils.timezone import now
 | 
			
		||||
 | 
			
		||||
from analysis.settings import LKOMPVZVO_PUBLISH_DATE
 | 
			
		||||
from analysis.utils.report import TimespanReport
 | 
			
		||||
from konova.sub_settings.django_settings import DEFAULT_DATE_FORMAT
 | 
			
		||||
from konova.tests.test_views import BaseTestCase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TimeSpanReportTestCase(BaseTestCase):
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
 | 
			
		||||
        today = now().date()
 | 
			
		||||
        old_date = LKOMPVZVO_PUBLISH_DATE - timedelta(days=1)
 | 
			
		||||
 | 
			
		||||
        self.conservation_office = self.get_conservation_office_code()
 | 
			
		||||
        self.eiv_old = self.create_dummy_intervention()
 | 
			
		||||
        self.kom_old = self.create_dummy_compensation(interv=self.eiv_old)
 | 
			
		||||
        self.assertNotEqual(self.compensation.intervention, self.kom_old.intervention)
 | 
			
		||||
        self.eiv = self.compensation.intervention
 | 
			
		||||
        self.oek_old = self.create_dummy_eco_account()
 | 
			
		||||
 | 
			
		||||
        self.eiv_old.responsible.conservation_office = self.conservation_office
 | 
			
		||||
        self.eiv_old.legal.binding_date = old_date
 | 
			
		||||
        self.eiv_old.legal.registration_date = old_date
 | 
			
		||||
 | 
			
		||||
        self.eiv.responsible.conservation_office = self.conservation_office
 | 
			
		||||
        self.eiv.legal.binding_date = today
 | 
			
		||||
        self.eiv.legal.registration_date = today
 | 
			
		||||
 | 
			
		||||
        self.eco_account.responsible.conservation_office = self.conservation_office
 | 
			
		||||
        self.eco_account.legal.registration_date = today
 | 
			
		||||
        self.eco_account.legal.binding_date = today
 | 
			
		||||
 | 
			
		||||
        self.oek_old.responsible.conservation_office = self.conservation_office
 | 
			
		||||
        self.oek_old.legal.registration_date = old_date
 | 
			
		||||
        self.oek_old.legal.binding_date = old_date
 | 
			
		||||
 | 
			
		||||
        self.eiv.legal.save()
 | 
			
		||||
        self.eiv.responsible.save()
 | 
			
		||||
 | 
			
		||||
        self.eiv_old.legal.save()
 | 
			
		||||
        self.eiv_old.responsible.save()
 | 
			
		||||
 | 
			
		||||
        self.eco_account.legal.save()
 | 
			
		||||
        self.eco_account.responsible.save()
 | 
			
		||||
 | 
			
		||||
        self.oek_old.legal.save()
 | 
			
		||||
        self.oek_old.responsible.save()
 | 
			
		||||
 | 
			
		||||
        self.deduction.account = self.eco_account
 | 
			
		||||
        self.deduction.intervention = self.eiv
 | 
			
		||||
        self.deduction.save()
 | 
			
		||||
 | 
			
		||||
    def test_init(self):
 | 
			
		||||
        date_from = now().date() - timedelta(days=365)
 | 
			
		||||
        date_to = now().date()
 | 
			
		||||
        report = TimespanReport(self.conservation_office.id, date_from, date_to)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(report.office_id, self.conservation_office.id)
 | 
			
		||||
        self.assertEqual(report.date_from, date_from)
 | 
			
		||||
        self.assertEqual(report.date_to, date_to)
 | 
			
		||||
 | 
			
		||||
        self.assertIsNotNone(report.intervention_report)
 | 
			
		||||
        self.assertIsNotNone(report.compensation_report)
 | 
			
		||||
        self.assertIsNotNone(report.eco_account_report)
 | 
			
		||||
        self.assertIsNotNone(report.old_data_report)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(report.excel_map["date_from"], date_from.strftime(DEFAULT_DATE_FORMAT))
 | 
			
		||||
        self.assertEqual(report.excel_map["date_to"], date_to.strftime(DEFAULT_DATE_FORMAT))
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(report.old_data_report.queryset_intervention_count, 1)
 | 
			
		||||
        self.assertEqual(report.old_data_report.queryset_intervention_recorded_count, 0)
 | 
			
		||||
        self.assertEqual(report.old_data_report.queryset_comps_count, 1)
 | 
			
		||||
        self.assertEqual(report.old_data_report.queryset_acc_count, 1)
 | 
			
		||||
        self.assertEqual(report.old_data_report.queryset_acc_recorded_count, 0)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(report.intervention_report.queryset_count, 1)
 | 
			
		||||
        self.assertEqual(report.intervention_report.queryset_checked_count, 0)
 | 
			
		||||
        self.assertEqual(report.intervention_report.queryset_recorded_count, 0)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(report.compensation_report.queryset_count, 1)
 | 
			
		||||
        self.assertEqual(report.compensation_report.queryset_checked_count, 0)
 | 
			
		||||
        self.assertEqual(report.compensation_report.queryset_recorded_count, 0)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(report.eco_account_report.queryset_count, 1)
 | 
			
		||||
        self.assertEqual(report.eco_account_report.queryset_recorded_count, 0)
 | 
			
		||||
        self.assertEqual(report.eco_account_report.queryset_deductions_count, 1)
 | 
			
		||||
        self.assertEqual(report.eco_account_report.queryset_deductions_recorded_count, 0)
 | 
			
		||||
@ -413,6 +413,7 @@ class TimespanReport:
 | 
			
		||||
        def __init__(self, id: str, date_from: str, date_to: str):
 | 
			
		||||
            # First fetch all eco account for this office
 | 
			
		||||
            self.queryset = EcoAccount.objects.filter(
 | 
			
		||||
                legal__registration_date__gt=LKOMPVZVO_PUBLISH_DATE,
 | 
			
		||||
                responsible__conservation_office__id=id,
 | 
			
		||||
                deleted=None,
 | 
			
		||||
                created__timestamp__date__gte=date_from,
 | 
			
		||||
@ -516,8 +517,8 @@ class TimespanReport:
 | 
			
		||||
                legal__registration_date__lte=LKOMPVZVO_PUBLISH_DATE,
 | 
			
		||||
                responsible__conservation_office__id=id,
 | 
			
		||||
                deleted=None,
 | 
			
		||||
                created__timestamp__gte=date_from,
 | 
			
		||||
                created__timestamp__lte=date_to,
 | 
			
		||||
                created__timestamp__date__gte=date_from,
 | 
			
		||||
                created__timestamp__date__lte=date_to,
 | 
			
		||||
            )
 | 
			
		||||
            self.queryset_acc_recorded = self.queryset_acc.filter(
 | 
			
		||||
                recorded__isnull=False,
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,7 @@ class APIUserToken(models.Model):
 | 
			
		||||
    valid_until = models.DateField(
 | 
			
		||||
        blank=True,
 | 
			
		||||
        null=True,
 | 
			
		||||
        help_text="Token is only valid until this date",
 | 
			
		||||
        help_text="Token is only valid until this date. Forever if null/blank.",
 | 
			
		||||
    )
 | 
			
		||||
    is_active = models.BooleanField(
 | 
			
		||||
        default=False,
 | 
			
		||||
@ -25,12 +25,11 @@ class APIUserToken(models.Model):
 | 
			
		||||
        return self.token
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def get_user_from_token(token: str, username: str):
 | 
			
		||||
    def get_user_from_token(token: str):
 | 
			
		||||
        """ Getter for the related user object
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            token (str): The used token
 | 
			
		||||
            username (str): The username
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            user (User): Otherwise None
 | 
			
		||||
@ -39,7 +38,6 @@ class APIUserToken(models.Model):
 | 
			
		||||
        try:
 | 
			
		||||
            token_obj = APIUserToken.objects.get(
 | 
			
		||||
                token=token,
 | 
			
		||||
                user__username=username
 | 
			
		||||
            )
 | 
			
		||||
            if not token_obj.is_active:
 | 
			
		||||
                raise PermissionError("Token unverified")
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										7
									
								
								api/tests/unit/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								api/tests/unit/__init__.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: ksp-servicestelle@sgdnord.rlp.de
 | 
			
		||||
Created on: 17.08.23
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
							
								
								
									
										71
									
								
								api/tests/unit/test_token.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								api/tests/unit/test_token.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,71 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: ksp-servicestelle@sgdnord.rlp.de
 | 
			
		||||
Created on: 17.08.23
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
from datetime import timedelta
 | 
			
		||||
 | 
			
		||||
from django.utils.timezone import now
 | 
			
		||||
 | 
			
		||||
from api.models import APIUserToken
 | 
			
		||||
from konova.tests.test_views import BaseTestCase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class APIUserTokenTestCase(BaseTestCase):
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
 | 
			
		||||
        self.token = APIUserToken.objects.create()
 | 
			
		||||
        self.superuser.api_token = self.token
 | 
			
		||||
        self.superuser.save()
 | 
			
		||||
 | 
			
		||||
    def test_str(self):
 | 
			
		||||
        self.assertEqual(str(self.token), self.token.token)
 | 
			
		||||
 | 
			
		||||
    def test_get_user_from_token(self):
 | 
			
		||||
        a_day = timedelta(days=1)
 | 
			
		||||
        today = now().date()
 | 
			
		||||
 | 
			
		||||
        self.assertFalse(self.token.is_active)
 | 
			
		||||
        self.assertIsNone(self.token.valid_until)
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            #Token not existing --> fail
 | 
			
		||||
            token_user = APIUserToken.get_user_from_token(self.token.token[::-1])
 | 
			
		||||
            self.fail("There should not have been any token")
 | 
			
		||||
        except PermissionError:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            # Token not active --> fail
 | 
			
		||||
            token_user = APIUserToken.get_user_from_token(self.token.token)
 | 
			
		||||
            self.fail("Token is unverified but token user has been fetchable.")
 | 
			
		||||
        except PermissionError:
 | 
			
		||||
            pass
 | 
			
		||||
        self.token.is_active = True
 | 
			
		||||
        self.token.valid_until = today - a_day
 | 
			
		||||
        self.token.save()
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            # Token valid until yesterday --> fail
 | 
			
		||||
            token_user = APIUserToken.get_user_from_token(self.token.token)
 | 
			
		||||
            self.fail("Token reached end of lifetime but token user has been fetchable.")
 | 
			
		||||
        except PermissionError:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        # Token valid until tomorrow --> success
 | 
			
		||||
        self.token.valid_until = today + a_day
 | 
			
		||||
        self.token.save()
 | 
			
		||||
 | 
			
		||||
        token_user = APIUserToken.get_user_from_token(self.token.token)
 | 
			
		||||
        self.assertEqual(token_user, self.superuser)
 | 
			
		||||
        del token_user
 | 
			
		||||
 | 
			
		||||
        # Token valid forever --> success
 | 
			
		||||
        self.token.valid_until = None
 | 
			
		||||
        self.token.save()
 | 
			
		||||
        token_user = APIUserToken.get_user_from_token(self.token.token)
 | 
			
		||||
        self.assertEqual(token_user, self.superuser)
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,6 @@ from django.urls import reverse
 | 
			
		||||
 | 
			
		||||
from konova.settings import DEFAULT_GROUP
 | 
			
		||||
from konova.tests.test_views import BaseTestCase
 | 
			
		||||
from konova.utils.user_checks import is_default_group_only
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BaseAPIV1TestCase(BaseTestCase):
 | 
			
		||||
@ -138,7 +137,7 @@ class APIV1SharingTestCase(BaseAPIV1TestCase):
 | 
			
		||||
        # Give the user only default group rights
 | 
			
		||||
        default_group = self.groups.get(name=DEFAULT_GROUP)
 | 
			
		||||
        self.superuser.groups.set([default_group])
 | 
			
		||||
        self.assertTrue(is_default_group_only(self.superuser))
 | 
			
		||||
        self.assertTrue(self.superuser.is_default_group_only())
 | 
			
		||||
 | 
			
		||||
        # Add only him as shared_users an object
 | 
			
		||||
        self.intervention.users.set([self.superuser])
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,6 @@ from compensation.models import EcoAccount
 | 
			
		||||
from ema.models import Ema
 | 
			
		||||
from intervention.models import Intervention
 | 
			
		||||
from konova.utils.message_templates import DATA_UNSHARED
 | 
			
		||||
from konova.utils.user_checks import is_default_group_only
 | 
			
		||||
from user.models import User, Team
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -53,7 +52,13 @@ class AbstractAPIView(View):
 | 
			
		||||
            # Fetch the proper user from the given request header token
 | 
			
		||||
            ksp_token = request.headers.get(KSP_TOKEN_HEADER_IDENTIFIER, None)
 | 
			
		||||
            ksp_user = request.headers.get(KSP_USER_HEADER_IDENTIFIER, None)
 | 
			
		||||
            self.user = APIUserToken.get_user_from_token(ksp_token, ksp_user)
 | 
			
		||||
            token_user = APIUserToken.get_user_from_token(ksp_token)
 | 
			
		||||
 | 
			
		||||
            if ksp_user != token_user.username:
 | 
			
		||||
                raise PermissionError(f"Invalid token for {ksp_user}")
 | 
			
		||||
            else:
 | 
			
		||||
                self.user = token_user
 | 
			
		||||
 | 
			
		||||
            request.user = self.user
 | 
			
		||||
            if not self.user.is_default_user():
 | 
			
		||||
                raise PermissionError("Default permissions required")
 | 
			
		||||
@ -315,7 +320,7 @@ class AbstractModelShareAPIView(AbstractAPIView):
 | 
			
		||||
        for team_name in new_teams:
 | 
			
		||||
            new_teams_objs.append(Team.objects.get(name=team_name))
 | 
			
		||||
 | 
			
		||||
        if is_default_group_only(self.user):
 | 
			
		||||
        if self.user.is_default_group_only():
 | 
			
		||||
            # Default only users are not allowed to remove other users from having access. They can only add new ones!
 | 
			
		||||
            new_users_to_be_added = User.objects.filter(
 | 
			
		||||
                username__in=new_users
 | 
			
		||||
 | 
			
		||||
@ -172,6 +172,23 @@ class EditEcoAccountForm(NewEcoAccountForm):
 | 
			
		||||
            disabled_fields
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def is_valid(self):
 | 
			
		||||
        valid = super().is_valid()
 | 
			
		||||
 | 
			
		||||
        deductable_surface = self.cleaned_data.get("surface")
 | 
			
		||||
        deduction_surface_sum = self.instance.get_deductions_surface()
 | 
			
		||||
        if deductable_surface < deduction_surface_sum:
 | 
			
		||||
            self.add_error(
 | 
			
		||||
                "surface",
 | 
			
		||||
                _("{}m² have been deducted from this eco account so far. The given value of {} would be too low.").format(
 | 
			
		||||
                    deduction_surface_sum,
 | 
			
		||||
                    deductable_surface
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
            valid &= False
 | 
			
		||||
 | 
			
		||||
        return valid
 | 
			
		||||
 | 
			
		||||
    def save(self, user: User, geom_form: SimpleGeomForm):
 | 
			
		||||
        with transaction.atomic():
 | 
			
		||||
            # Fetch data from cleaned POST values
 | 
			
		||||
 | 
			
		||||
@ -93,7 +93,7 @@ class NewCompensationActionModalForm(BaseModalForm):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.form_title = _("New action")
 | 
			
		||||
        self.form_caption = _("Insert data for the new action")
 | 
			
		||||
        choices =KonovaCode.objects.filter(
 | 
			
		||||
        choices = KonovaCode.objects.filter(
 | 
			
		||||
            code_lists__in=[CODELIST_COMPENSATION_ACTION_ID],
 | 
			
		||||
            is_archived=False,
 | 
			
		||||
            is_leaf=True,
 | 
			
		||||
 | 
			
		||||
@ -77,8 +77,11 @@ class NewPaymentForm(BaseModalForm):
 | 
			
		||||
            is_valid (bool): True if valid, False otherwise
 | 
			
		||||
        """
 | 
			
		||||
        super_valid = super().is_valid()
 | 
			
		||||
        date = self.cleaned_data["due"]
 | 
			
		||||
        comment = self.cleaned_data["comment"] or None
 | 
			
		||||
        if not super_valid:
 | 
			
		||||
            return super_valid
 | 
			
		||||
 | 
			
		||||
        date = self.cleaned_data.get("due", None)
 | 
			
		||||
        comment = self.cleaned_data.get("comment", None)
 | 
			
		||||
        if not date and not comment:
 | 
			
		||||
            # At least one needs to be set!
 | 
			
		||||
            self.add_error(
 | 
			
		||||
 | 
			
		||||
@ -10,6 +10,7 @@ import shutil
 | 
			
		||||
from django.contrib import messages
 | 
			
		||||
 | 
			
		||||
from codelist.models import KonovaCode
 | 
			
		||||
from compensation.settings import COMPENSATION_IDENTIFIER_TEMPLATE, COMPENSATION_IDENTIFIER_LENGTH
 | 
			
		||||
from user.models import User, Team
 | 
			
		||||
from django.db import models, transaction
 | 
			
		||||
from django.db.models import QuerySet, Sum
 | 
			
		||||
@ -298,6 +299,9 @@ class Compensation(AbstractCompensation, CEFMixin, CoherenceMixin, PikMixin):
 | 
			
		||||
 | 
			
		||||
    objects = CompensationManager()
 | 
			
		||||
 | 
			
		||||
    identifier_length = COMPENSATION_IDENTIFIER_LENGTH
 | 
			
		||||
    identifier_template = COMPENSATION_IDENTIFIER_TEMPLATE
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return "{}".format(self.identifier)
 | 
			
		||||
 | 
			
		||||
@ -395,7 +399,7 @@ class Compensation(AbstractCompensation, CEFMixin, CoherenceMixin, PikMixin):
 | 
			
		||||
        Returns:
 | 
			
		||||
            users (QuerySet)
 | 
			
		||||
        """
 | 
			
		||||
        return self.intervention.users.all()
 | 
			
		||||
        return self.intervention.shared_users
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def shared_teams(self) -> QuerySet:
 | 
			
		||||
@ -404,7 +408,7 @@ class Compensation(AbstractCompensation, CEFMixin, CoherenceMixin, PikMixin):
 | 
			
		||||
        Returns:
 | 
			
		||||
            users (QuerySet)
 | 
			
		||||
        """
 | 
			
		||||
        return self.intervention.teams.all()
 | 
			
		||||
        return self.intervention.shared_teams
 | 
			
		||||
 | 
			
		||||
    def get_documents(self) -> QuerySet:
 | 
			
		||||
        """ Getter for all documents of a compensation
 | 
			
		||||
@ -417,19 +421,18 @@ class Compensation(AbstractCompensation, CEFMixin, CoherenceMixin, PikMixin):
 | 
			
		||||
        )
 | 
			
		||||
        return docs
 | 
			
		||||
 | 
			
		||||
    def mark_as_edited(self, user: User, request: HttpRequest = None, edit_comment: str = None, reset_recorded: bool = True):
 | 
			
		||||
        """ Performs internal logic for setting the recordedd/checked state of the related intervention
 | 
			
		||||
    def mark_as_edited(self, user: User, request: HttpRequest = None, edit_comment: str = None):
 | 
			
		||||
        """ Performs internal logic for setting the checked state of the related intervention
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            user (User): The performing user
 | 
			
		||||
            request (HttpRequest): The performing request
 | 
			
		||||
            edit_comment (str): Additional comment for the log entry
 | 
			
		||||
            reset_recorded (bool): Whether the record-state of the object should be reset
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        self.intervention.unrecord(user, request)
 | 
			
		||||
        self.intervention.set_unchecked()
 | 
			
		||||
        action = super().mark_as_edited(user, edit_comment=edit_comment)
 | 
			
		||||
        return action
 | 
			
		||||
 | 
			
		||||
@ -509,8 +512,11 @@ class CompensationDocument(AbstractDocument):
 | 
			
		||||
            # The only file left for this compensation is the one which is currently processed and will be deleted
 | 
			
		||||
            # Make sure that the compensation folder itself is deleted as well, not only the file
 | 
			
		||||
            # Therefore take the folder path from the file path
 | 
			
		||||
            folder_path = self.file.path.split("/")[:-1]
 | 
			
		||||
            folder_path = "/".join(folder_path)
 | 
			
		||||
            try:
 | 
			
		||||
                folder_path = self.file.path.split("/")[:-1]
 | 
			
		||||
                folder_path = "/".join(folder_path)
 | 
			
		||||
            except ValueError:
 | 
			
		||||
                folder_path = None
 | 
			
		||||
 | 
			
		||||
        if user:
 | 
			
		||||
            self.instance.mark_as_edited(user, edit_comment=DOCUMENT_REMOVED_TEMPLATE.format(self.title))
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,7 @@ import shutil
 | 
			
		||||
 | 
			
		||||
from django.urls import reverse
 | 
			
		||||
 | 
			
		||||
from compensation.settings import ECO_ACCOUNT_IDENTIFIER_TEMPLATE, ECO_ACCOUNT_IDENTIFIER_LENGTH
 | 
			
		||||
from konova.utils.message_templates import DEDUCTION_REMOVED, DOCUMENT_REMOVED_TEMPLATE
 | 
			
		||||
from django.core.exceptions import ValidationError
 | 
			
		||||
from django.core.validators import MinValueValidator
 | 
			
		||||
@ -52,23 +53,12 @@ class EcoAccount(AbstractCompensation, ShareableObjectMixin, RecordableObjectMix
 | 
			
		||||
 | 
			
		||||
    objects = EcoAccountManager()
 | 
			
		||||
 | 
			
		||||
    identifier_length = ECO_ACCOUNT_IDENTIFIER_LENGTH
 | 
			
		||||
    identifier_template = ECO_ACCOUNT_IDENTIFIER_TEMPLATE
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return f"{self.identifier} ({self.title})"
 | 
			
		||||
 | 
			
		||||
    def clean(self):
 | 
			
		||||
        # Deductable surface can not be larger than added states after surface
 | 
			
		||||
        after_state_sum = self.get_surface_after_states()
 | 
			
		||||
        if self.deductable_surface > after_state_sum:
 | 
			
		||||
            raise ValidationError(_("Deductable surface can not be larger than existing surfaces in after states"))
 | 
			
		||||
 | 
			
		||||
        # Deductable surface can not be lower than amount of already deducted surfaces
 | 
			
		||||
        # User needs to contact deducting user in case of further problems
 | 
			
		||||
        deducted_sum = self.get_deductions_surface()
 | 
			
		||||
        if self.deductable_surface < deducted_sum:
 | 
			
		||||
            raise ValidationError(
 | 
			
		||||
                _("Deductable surface can not be smaller than the sum of already existing deductions. Please contact the responsible users for the deductions!")
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    def save(self, *args, **kwargs):
 | 
			
		||||
        if self.identifier is None or len(self.identifier) == 0:
 | 
			
		||||
            # Create new identifier if none was given
 | 
			
		||||
@ -236,8 +226,11 @@ class EcoAccountDocument(AbstractDocument):
 | 
			
		||||
            # The only file left for this eco account is the one which is currently processed and will be deleted
 | 
			
		||||
            # Make sure that the compensation folder itself is deleted as well, not only the file
 | 
			
		||||
            # Therefore take the folder path from the file path
 | 
			
		||||
            folder_path = self.file.path.split("/")[:-1]
 | 
			
		||||
            folder_path = "/".join(folder_path)
 | 
			
		||||
            try:
 | 
			
		||||
                folder_path = self.file.path.split("/")[:-1]
 | 
			
		||||
                folder_path = "/".join(folder_path)
 | 
			
		||||
            except ValueError:
 | 
			
		||||
                folder_path = None
 | 
			
		||||
 | 
			
		||||
        if user:
 | 
			
		||||
            self.instance.mark_as_edited(user, edit_comment=DOCUMENT_REMOVED_TEMPLATE.format(self.title))
 | 
			
		||||
 | 
			
		||||
@ -10,8 +10,6 @@ from django.db import models
 | 
			
		||||
 | 
			
		||||
from intervention.models import Intervention
 | 
			
		||||
from konova.models import BaseResource
 | 
			
		||||
from konova.utils.message_templates import PAYMENT_REMOVED
 | 
			
		||||
from user.models import UserActionLogEntry
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Payment(BaseResource):
 | 
			
		||||
 | 
			
		||||
@ -244,6 +244,7 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase):
 | 
			
		||||
        self.client_user.post(record_url, post_data)
 | 
			
		||||
 | 
			
		||||
        # Check that the intervention is still not recorded
 | 
			
		||||
        self.intervention.refresh_from_db()
 | 
			
		||||
        self.assertIsNone(self.intervention.recorded)
 | 
			
		||||
 | 
			
		||||
        # Now fill out the data for a compensation
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										7
									
								
								compensation/tests/compensation/unit/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								compensation/tests/compensation/unit/__init__.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: ksp-servicestelle@sgdnord.rlp.de
 | 
			
		||||
Created on: 21.08.23
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
							
								
								
									
										318
									
								
								compensation/tests/compensation/unit/test_forms.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										318
									
								
								compensation/tests/compensation/unit/test_forms.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,318 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: ksp-servicestelle@sgdnord.rlp.de
 | 
			
		||||
Created on: 21.08.23
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
from django.core.exceptions import ObjectDoesNotExist
 | 
			
		||||
from django.test import RequestFactory
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from codelist.models import KonovaCodeList
 | 
			
		||||
from codelist.settings import CODELIST_COMPENSATION_ACTION_ID, CODELIST_BIOTOPES_ID
 | 
			
		||||
from compensation.forms.modals.compensation_action import NewCompensationActionModalForm, \
 | 
			
		||||
    EditCompensationActionModalForm, RemoveCompensationActionModalForm
 | 
			
		||||
from compensation.forms.modals.state import NewCompensationStateModalForm, EditCompensationStateModalForm, \
 | 
			
		||||
    RemoveCompensationStateModalForm
 | 
			
		||||
from compensation.models import UnitChoices
 | 
			
		||||
from konova.tests.test_views import BaseTestCase
 | 
			
		||||
from konova.utils.generators import generate_random_string
 | 
			
		||||
from konova.utils.message_templates import COMPENSATION_ACTION_EDITED, ADDED_COMPENSATION_ACTION, \
 | 
			
		||||
    COMPENSATION_ACTION_REMOVED, ADDED_COMPENSATION_STATE, COMPENSATION_STATE_EDITED, \
 | 
			
		||||
    COMPENSATION_STATE_REMOVED
 | 
			
		||||
from user.models import UserAction
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NewCompensationActionModalFormTestCase(BaseTestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
        self.request = RequestFactory().request()
 | 
			
		||||
        self.request.user = self.superuser
 | 
			
		||||
 | 
			
		||||
        self.action_dummy_code = self.create_dummy_codes().first()
 | 
			
		||||
        action_list = KonovaCodeList.objects.get_or_create(
 | 
			
		||||
            id=CODELIST_COMPENSATION_ACTION_ID,
 | 
			
		||||
        )[0]
 | 
			
		||||
        action_list.codes.add(self.action_dummy_code)
 | 
			
		||||
 | 
			
		||||
    def test_init(self):
 | 
			
		||||
        form = NewCompensationActionModalForm()
 | 
			
		||||
        self.assertEqual(form.form_title, str(_("New action")))
 | 
			
		||||
        self.assertEqual(form.form_caption, str(_("Insert data for the new action")))
 | 
			
		||||
        self.assertTrue(len(form.fields["action_type"].choices) == 1)
 | 
			
		||||
 | 
			
		||||
    def test_save(self):
 | 
			
		||||
        comment = "TEST_comment"
 | 
			
		||||
        unit = UnitChoices.km
 | 
			
		||||
        amount = 2.5
 | 
			
		||||
 | 
			
		||||
        data = {
 | 
			
		||||
            "action_type": [self.action_dummy_code.id],
 | 
			
		||||
            "action_type_details": [],
 | 
			
		||||
            "unit": unit,
 | 
			
		||||
            "amount": amount,
 | 
			
		||||
            "comment": comment,
 | 
			
		||||
        }
 | 
			
		||||
        form = NewCompensationActionModalForm(data, request=self.request, instance=self.compensation)
 | 
			
		||||
        self.assertTrue(form.is_valid(), msg=form.errors)
 | 
			
		||||
 | 
			
		||||
        comp_action = form.save()
 | 
			
		||||
        last_log = self.compensation.log.first()
 | 
			
		||||
        self.assertIn(comp_action, self.compensation.actions.all())
 | 
			
		||||
        self.assertEqual(last_log.action, UserAction.EDITED)
 | 
			
		||||
        self.assertEqual(last_log.user, self.superuser)
 | 
			
		||||
        self.assertEqual(last_log.comment, ADDED_COMPENSATION_ACTION)
 | 
			
		||||
        self.assertEqual(comp_action.amount, amount)
 | 
			
		||||
        self.assertEqual(comp_action.unit, unit)
 | 
			
		||||
        self.assertEqual(comp_action.comment, comment)
 | 
			
		||||
        comp_action_types = comp_action.action_type.all()
 | 
			
		||||
        self.assertEqual(comp_action_types.count(), 1)
 | 
			
		||||
        self.assertEqual(comp_action_types.first(), self.action_dummy_code)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EditCompensationActionModalFormTestCase(NewCompensationActionModalFormTestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
        self.comp_action = self.create_dummy_action()
 | 
			
		||||
        self.compensation.actions.add(self.comp_action)
 | 
			
		||||
 | 
			
		||||
    def test_init(self):
 | 
			
		||||
        form = EditCompensationActionModalForm(request=self.request, instance=self.compensation, action=self.comp_action)
 | 
			
		||||
        self.assertEqual(form.form_title, str(_("Edit action")))
 | 
			
		||||
        self.assertEqual(len(form.fields["action_type"].initial), self.comp_action.action_type.count())
 | 
			
		||||
        self.assertEqual(len(form.fields["action_type_details"].initial), self.comp_action.action_type_details.count())
 | 
			
		||||
        self.assertEqual(form.fields["amount"].initial, self.comp_action.amount)
 | 
			
		||||
        self.assertEqual(form.fields["unit"].initial, self.comp_action.unit)
 | 
			
		||||
        self.assertEqual(form.fields["comment"].initial, self.comp_action.comment)
 | 
			
		||||
 | 
			
		||||
    def test_save(self):
 | 
			
		||||
        amount = 25.4
 | 
			
		||||
        unit = UnitChoices.cm
 | 
			
		||||
        comment = generate_random_string(length=20, use_numbers=True, use_letters_lc=True, use_letters_uc=True)
 | 
			
		||||
 | 
			
		||||
        data = {
 | 
			
		||||
            "action_type": [self.action_dummy_code.id],
 | 
			
		||||
            "action_type_details": [],
 | 
			
		||||
            "amount": amount,
 | 
			
		||||
            "unit": unit,
 | 
			
		||||
            "comment": comment,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        form = EditCompensationActionModalForm(data, request=self.request, instance=self.compensation, action=self.comp_action)
 | 
			
		||||
 | 
			
		||||
        self.assertTrue(form.is_valid())
 | 
			
		||||
        action = form.save()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(action.action_type.count(), len(data["action_type"]))
 | 
			
		||||
        self.assertEqual(action.action_type_details.count(), 0)
 | 
			
		||||
        self.assertEqual(float(action.amount), amount)
 | 
			
		||||
        self.assertEqual(action.unit, unit)
 | 
			
		||||
        self.assertEqual(action.comment, comment)
 | 
			
		||||
 | 
			
		||||
        last_log = self.compensation.log.first()
 | 
			
		||||
        self.assertEqual(last_log.action, UserAction.EDITED)
 | 
			
		||||
        self.assertEqual(last_log.user, self.superuser)
 | 
			
		||||
        self.assertEqual(last_log.comment, COMPENSATION_ACTION_EDITED)
 | 
			
		||||
        self.assertIn(action, self.compensation.actions.all())
 | 
			
		||||
        self.assertEqual(self.compensation.actions.count(), 1)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RemoveCompensationActionModalFormTestCase(EditCompensationActionModalFormTestCase):
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
        
 | 
			
		||||
    def test_init(self):
 | 
			
		||||
        self.assertIn(self.comp_action, self.compensation.actions.all())
 | 
			
		||||
        form = RemoveCompensationActionModalForm(request=self.request, instance=self.compensation, action=self.comp_action)
 | 
			
		||||
        self.assertEqual(form.action, self.comp_action)
 | 
			
		||||
        
 | 
			
		||||
    def test_save(self):
 | 
			
		||||
        data = {
 | 
			
		||||
            "confirm": True,
 | 
			
		||||
        }
 | 
			
		||||
        form = RemoveCompensationActionModalForm(
 | 
			
		||||
            data, 
 | 
			
		||||
            request=self.request,
 | 
			
		||||
            instance=self.compensation,
 | 
			
		||||
            action=self.comp_action
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(form.is_valid())
 | 
			
		||||
        self.assertIn(self.comp_action, self.compensation.actions.all())
 | 
			
		||||
 | 
			
		||||
        form.save()
 | 
			
		||||
 | 
			
		||||
        last_log = self.compensation.log.first()
 | 
			
		||||
        self.assertEqual(last_log.action, UserAction.EDITED)
 | 
			
		||||
        self.assertEqual(last_log.user, self.superuser)
 | 
			
		||||
        self.assertEqual(last_log.comment, COMPENSATION_ACTION_REMOVED)
 | 
			
		||||
 | 
			
		||||
        self.assertNotIn(self.comp_action, self.compensation.actions.all())
 | 
			
		||||
        try:
 | 
			
		||||
            self.comp_action.refresh_from_db()
 | 
			
		||||
            self.fail(msg="This action should not be fetchable anymore")
 | 
			
		||||
        except ObjectDoesNotExist:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NewCompensationStateModalFormTestCase(BaseTestCase):
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
        self.request = RequestFactory().request()
 | 
			
		||||
        self.request.user = self.superuser
 | 
			
		||||
 | 
			
		||||
        self.comp_biotope_code = self.create_dummy_codes().first()
 | 
			
		||||
        self.biotope_codelist = KonovaCodeList.objects.get_or_create(
 | 
			
		||||
            id=CODELIST_BIOTOPES_ID
 | 
			
		||||
        )[0]
 | 
			
		||||
        self.biotope_codelist.codes.add(self.comp_biotope_code)
 | 
			
		||||
 | 
			
		||||
    def test_init(self):
 | 
			
		||||
        form = NewCompensationStateModalForm(request=self.request, instance=self.compensation)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(form.form_title, str(_("New state")))
 | 
			
		||||
        self.assertEqual(form.form_caption, str(_("Insert data for the new state")))
 | 
			
		||||
        self.assertEqual(len(form.fields["biotope_type"].choices), 1)
 | 
			
		||||
 | 
			
		||||
    def test_save(self):
 | 
			
		||||
        test_surface = 123.45
 | 
			
		||||
        data = {
 | 
			
		||||
            "biotope_type": self.comp_biotope_code.id,
 | 
			
		||||
            "biotope_extra": [],
 | 
			
		||||
            "surface": test_surface,
 | 
			
		||||
        }
 | 
			
		||||
        self.assertEqual(self.compensation.before_states.count(), 0)
 | 
			
		||||
        self.assertEqual(self.compensation.after_states.count(), 0)
 | 
			
		||||
 | 
			
		||||
        form = NewCompensationStateModalForm(data, request=self.request, instance=self.compensation)
 | 
			
		||||
 | 
			
		||||
        self.assertTrue(form.is_valid(), msg=form.errors)
 | 
			
		||||
 | 
			
		||||
        is_before_state = True
 | 
			
		||||
        state = form.save(is_before_state)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(self.compensation.before_states.count(), 1)
 | 
			
		||||
        self.assertEqual(self.compensation.after_states.count(), 0)
 | 
			
		||||
        self.assertIn(state, self.compensation.before_states.all())
 | 
			
		||||
        self.assertEqual(state.biotope_type, self.comp_biotope_code)
 | 
			
		||||
        self.assertEqual(state.biotope_type_details.count(), 0)
 | 
			
		||||
        self.assertEqual(float(state.surface), test_surface)
 | 
			
		||||
 | 
			
		||||
        last_log = self.compensation.log.first()
 | 
			
		||||
        self.assertEqual(last_log.user, self.superuser)
 | 
			
		||||
        self.assertEqual(last_log.action, UserAction.EDITED)
 | 
			
		||||
        self.assertEqual(last_log.comment, ADDED_COMPENSATION_STATE)
 | 
			
		||||
 | 
			
		||||
        is_before_state = False
 | 
			
		||||
        state = form.save(is_before_state)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(self.compensation.before_states.count(), 1)
 | 
			
		||||
        self.assertEqual(self.compensation.after_states.count(), 1)
 | 
			
		||||
        self.assertIn(state, self.compensation.after_states.all())
 | 
			
		||||
        self.assertEqual(state.biotope_type, self.comp_biotope_code)
 | 
			
		||||
        self.assertEqual(state.biotope_type_details.count(), 0)
 | 
			
		||||
        self.assertEqual(float(state.surface), test_surface)
 | 
			
		||||
 | 
			
		||||
        last_log = self.compensation.log.first()
 | 
			
		||||
        self.assertEqual(last_log.user, self.superuser)
 | 
			
		||||
        self.assertEqual(last_log.action, UserAction.EDITED)
 | 
			
		||||
        self.assertEqual(last_log.comment, ADDED_COMPENSATION_STATE)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EditCompensationStateModalFormTestCase(NewCompensationStateModalFormTestCase):
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
        self.comp_state.biotope_type = self.comp_biotope_code
 | 
			
		||||
        self.comp_state.save()
 | 
			
		||||
 | 
			
		||||
        self.compensation.after_states.add(self.comp_state)
 | 
			
		||||
 | 
			
		||||
    def test_init(self):
 | 
			
		||||
        form = EditCompensationStateModalForm(request=self.request, instance=self.compensation, state=self.comp_state)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(form.state, self.comp_state)
 | 
			
		||||
        self.assertEqual(form.form_title, str(_("Edit state")))
 | 
			
		||||
        self.assertEqual(form.fields["biotope_type"].initial, self.comp_state.biotope_type.id)
 | 
			
		||||
        self.assertTrue(
 | 
			
		||||
            form.fields["biotope_extra"].initial.difference(
 | 
			
		||||
                self.comp_state.biotope_type_details.all()
 | 
			
		||||
            ).count() == 0
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(form.fields["surface"].initial, self.comp_state.surface)
 | 
			
		||||
 | 
			
		||||
    def test_save(self):
 | 
			
		||||
        test_surface = 987.65
 | 
			
		||||
        test_code = self.create_dummy_codes().exclude(
 | 
			
		||||
            id=self.comp_biotope_code.id
 | 
			
		||||
        ).first()
 | 
			
		||||
        self.biotope_codelist.codes.add(test_code)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(self.compensation.after_states.count(), 1)
 | 
			
		||||
        self.assertEqual(self.compensation.before_states.count(), 0)
 | 
			
		||||
 | 
			
		||||
        data = {
 | 
			
		||||
            "biotope_type": test_code.id,
 | 
			
		||||
            "biotope_extra": [],
 | 
			
		||||
            "surface": test_surface,
 | 
			
		||||
        }
 | 
			
		||||
        form = EditCompensationStateModalForm(
 | 
			
		||||
            data,
 | 
			
		||||
            request=self.request,
 | 
			
		||||
            instance=self.compensation,
 | 
			
		||||
            state=self.comp_state
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(form.is_valid(), msg=form.errors)
 | 
			
		||||
 | 
			
		||||
        is_before_state = False
 | 
			
		||||
        state = form.save(is_before_state=is_before_state)
 | 
			
		||||
        self.assertEqual(state.biotope_type, test_code)
 | 
			
		||||
        self.assertEqual(state.biotope_type_details.count(), 0)
 | 
			
		||||
        self.assertEqual(float(state.surface), test_surface)
 | 
			
		||||
 | 
			
		||||
        last_log = self.compensation.log.first()
 | 
			
		||||
        self.assertEqual(last_log.action, UserAction.EDITED)
 | 
			
		||||
        self.assertEqual(last_log.user, self.superuser)
 | 
			
		||||
        self.assertEqual(last_log.comment, COMPENSATION_STATE_EDITED)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RemoveCompensationStateModalFormTestCase(EditCompensationStateModalFormTestCase):
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
 | 
			
		||||
    def test_init(self):
 | 
			
		||||
        form = RemoveCompensationStateModalForm(request=self.request, instance=self.compensation, state=self.comp_state)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(form.state, self.comp_state)
 | 
			
		||||
 | 
			
		||||
    def test_save(self):
 | 
			
		||||
        data = {
 | 
			
		||||
            "confirm": True
 | 
			
		||||
        }
 | 
			
		||||
        form = RemoveCompensationStateModalForm(
 | 
			
		||||
            data,
 | 
			
		||||
            request=self.request,
 | 
			
		||||
            instance=self.compensation,
 | 
			
		||||
            state=self.comp_state
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(form.is_valid(), msg=form.errors)
 | 
			
		||||
 | 
			
		||||
        self.assertIn(self.comp_state, self.compensation.after_states.all())
 | 
			
		||||
        self.assertNotIn(self.comp_state, self.compensation.before_states.all())
 | 
			
		||||
 | 
			
		||||
        form.save()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(self.compensation.after_states.count(), 0)
 | 
			
		||||
        self.assertEqual(self.compensation.before_states.count(), 0)
 | 
			
		||||
        try:
 | 
			
		||||
            self.comp_state.refresh_from_db()
 | 
			
		||||
            self.fail("Entry should not existing anymore")
 | 
			
		||||
        except ObjectDoesNotExist:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        last_log = self.compensation.log.first()
 | 
			
		||||
        self.assertEqual(last_log.user, self.superuser)
 | 
			
		||||
        self.assertEqual(last_log.action, UserAction.EDITED)
 | 
			
		||||
        self.assertEqual(last_log.comment, COMPENSATION_STATE_REMOVED)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										201
									
								
								compensation/tests/compensation/unit/test_models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								compensation/tests/compensation/unit/test_models.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,201 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: ksp-servicestelle@sgdnord.rlp.de
 | 
			
		||||
Created on: 30.08.23
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
from django.core.exceptions import ObjectDoesNotExist
 | 
			
		||||
from django.test import RequestFactory
 | 
			
		||||
from django.utils.timezone import now
 | 
			
		||||
 | 
			
		||||
from compensation.forms.modals.deadline import NewDeadlineModalForm
 | 
			
		||||
from compensation.models import CompensationDocument
 | 
			
		||||
from konova.forms.modals import RemoveDeadlineModalForm
 | 
			
		||||
from konova.models import DeadlineType
 | 
			
		||||
from konova.tests.test_views import BaseTestCase
 | 
			
		||||
from konova.utils.message_templates import DEADLINE_REMOVED, DOCUMENT_REMOVED_TEMPLATE, COMPENSATION_REMOVED_TEMPLATE, \
 | 
			
		||||
    DEADLINE_ADDED
 | 
			
		||||
from user.models import UserAction, Team
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AbstractCompensationModelTestCase(BaseTestCase):
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
        self.request = RequestFactory().request()
 | 
			
		||||
        self.request.user = self.superuser
 | 
			
		||||
 | 
			
		||||
    def test_remove_deadline(self):
 | 
			
		||||
        self.compensation.deadlines.add(self.finished_deadline)
 | 
			
		||||
 | 
			
		||||
        data = {
 | 
			
		||||
            "confirm": True
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        form = RemoveDeadlineModalForm(
 | 
			
		||||
            data,
 | 
			
		||||
            request=self.request,
 | 
			
		||||
            instance=self.compensation,
 | 
			
		||||
            deadline=self.finished_deadline,
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(form.is_valid(), msg=form.errors)
 | 
			
		||||
        self.assertIn(self.finished_deadline, self.compensation.deadlines.all())
 | 
			
		||||
        form.save()
 | 
			
		||||
 | 
			
		||||
        last_log = self.compensation.log.first()
 | 
			
		||||
        self.assertEqual(last_log.user, self.request.user)
 | 
			
		||||
        self.assertEqual(last_log.action, UserAction.EDITED)
 | 
			
		||||
        self.assertEqual(last_log.comment, DEADLINE_REMOVED)
 | 
			
		||||
 | 
			
		||||
        self.assertNotIn(self.finished_deadline, self.compensation.deadlines.all())
 | 
			
		||||
        try:
 | 
			
		||||
            self.finished_deadline.refresh_from_db()
 | 
			
		||||
            self.fail("Deadline should not exist anymore after removing from abstract compensation")
 | 
			
		||||
        except ObjectDoesNotExist:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
    def test_add_deadline(self):
 | 
			
		||||
        request = RequestFactory().request()
 | 
			
		||||
        request.user = self.superuser
 | 
			
		||||
 | 
			
		||||
        data = {
 | 
			
		||||
            "type": DeadlineType.MAINTAIN,
 | 
			
		||||
            "date": now().date(),
 | 
			
		||||
            "comment": "TestDeadline"
 | 
			
		||||
        }
 | 
			
		||||
        form = NewDeadlineModalForm(
 | 
			
		||||
            data,
 | 
			
		||||
            request=self.request,
 | 
			
		||||
            instance=self.compensation,
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(form.is_valid(), msg=form.errors)
 | 
			
		||||
        deadline = self.compensation.add_deadline(form)
 | 
			
		||||
        self.assertEqual(deadline.date, data["date"])
 | 
			
		||||
        self.assertEqual(deadline.type, data["type"])
 | 
			
		||||
        self.assertEqual(deadline.comment, data["comment"])
 | 
			
		||||
        self.assertEqual(deadline.created.action, UserAction.CREATED)
 | 
			
		||||
        self.assertEqual(deadline.created.user, self.superuser)
 | 
			
		||||
        self.assertEqual(deadline.created.comment, None)
 | 
			
		||||
        self.assertIn(deadline, self.compensation.deadlines.all())
 | 
			
		||||
 | 
			
		||||
        last_log = self.compensation.log.first()
 | 
			
		||||
        self.assertEqual(last_log.action, UserAction.EDITED)
 | 
			
		||||
        self.assertEqual(last_log.user, self.superuser)
 | 
			
		||||
        self.assertEqual(last_log.comment, DEADLINE_ADDED)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CompensationTestCase(BaseTestCase):
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
 | 
			
		||||
    def test_str(self):
 | 
			
		||||
        self.assertEqual(str(self.compensation), self.compensation.identifier)
 | 
			
		||||
 | 
			
		||||
    def test_save(self):
 | 
			
		||||
        old_identifier = self.compensation.identifier
 | 
			
		||||
        self.compensation.identifier = None
 | 
			
		||||
        self.compensation.save()
 | 
			
		||||
        self.assertIsNotNone(self.compensation.identifier)
 | 
			
		||||
        self.assertNotEqual(old_identifier, self.compensation.identifier)
 | 
			
		||||
 | 
			
		||||
    def test_share_with_user(self):
 | 
			
		||||
        self.assertNotIn(self.user, self.compensation.shared_users)
 | 
			
		||||
        self.compensation.share_with_user(self.user)
 | 
			
		||||
        self.assertIn(self.user, self.compensation.shared_users)
 | 
			
		||||
 | 
			
		||||
    def test_share_with_user_list(self):
 | 
			
		||||
        user_list = [
 | 
			
		||||
            self.user
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertNotIn(self.user, self.compensation.shared_users)
 | 
			
		||||
        self.compensation.share_with_user_list(user_list)
 | 
			
		||||
        self.assertIn(self.user, self.compensation.shared_users)
 | 
			
		||||
        user_list = [
 | 
			
		||||
            self.superuser
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertNotIn(self.superuser, self.compensation.shared_users)
 | 
			
		||||
        self.compensation.share_with_user_list(user_list)
 | 
			
		||||
        self.assertIn(self.superuser, self.compensation.shared_users)
 | 
			
		||||
        self.assertNotIn(self.user, self.compensation.shared_users)
 | 
			
		||||
 | 
			
		||||
    def test_share_with_team(self):
 | 
			
		||||
        self.assertNotIn(self.team, self.compensation.shared_teams)
 | 
			
		||||
        self.compensation.share_with_team(self.team)
 | 
			
		||||
        self.assertIn(self.team, self.compensation.shared_teams)
 | 
			
		||||
 | 
			
		||||
    def test_share_with_team_list(self):
 | 
			
		||||
        self.compensation.share_with_team(self.team)
 | 
			
		||||
        self.assertIn(self.team, self.compensation.shared_teams)
 | 
			
		||||
        other_team = Team.objects.create(
 | 
			
		||||
            name="NewTeam"
 | 
			
		||||
        )
 | 
			
		||||
        team_list = [
 | 
			
		||||
            other_team
 | 
			
		||||
        ]
 | 
			
		||||
        self.compensation.share_with_team_list(team_list)
 | 
			
		||||
        self.assertIn(other_team, self.compensation.shared_teams)
 | 
			
		||||
        self.assertNotIn(self.team, self.compensation.shared_teams)
 | 
			
		||||
 | 
			
		||||
    def test_shared_users(self):
 | 
			
		||||
        intervention = self.compensation.intervention
 | 
			
		||||
        diff = self.compensation.shared_users.difference(intervention.shared_users)
 | 
			
		||||
        self.assertEqual(diff.count(), 0)
 | 
			
		||||
 | 
			
		||||
        self.compensation.share_with_user(self.superuser)
 | 
			
		||||
        diff = self.compensation.shared_users.difference(intervention.shared_users)
 | 
			
		||||
        self.assertEqual(diff.count(), 0)
 | 
			
		||||
 | 
			
		||||
    def test_shared_teams(self):
 | 
			
		||||
        intervention = self.compensation.intervention
 | 
			
		||||
        diff = self.compensation.shared_users.difference(intervention.shared_users)
 | 
			
		||||
        self.assertEqual(diff.count(), 0)
 | 
			
		||||
 | 
			
		||||
        self.compensation.share_with_user(self.superuser)
 | 
			
		||||
        diff = self.compensation.shared_users.difference(intervention.shared_users)
 | 
			
		||||
        self.assertEqual(diff.count(), 0)
 | 
			
		||||
 | 
			
		||||
    def test_get_documents(self):
 | 
			
		||||
        doc = self.create_dummy_document(CompensationDocument, self.compensation)
 | 
			
		||||
        docs = self.compensation.get_documents()
 | 
			
		||||
        self.assertIn(doc, docs)
 | 
			
		||||
 | 
			
		||||
    def test_mark_as_deleted(self):
 | 
			
		||||
        self.assertIsNone(self.compensation.deleted)
 | 
			
		||||
        self.compensation.mark_as_deleted(self.superuser, send_mail=False)
 | 
			
		||||
 | 
			
		||||
        comp_deleted = self.compensation.deleted
 | 
			
		||||
        self.assertIsNotNone(comp_deleted)
 | 
			
		||||
        self.assertEqual(comp_deleted.action, UserAction.DELETED)
 | 
			
		||||
        self.assertEqual(comp_deleted.user, self.superuser)
 | 
			
		||||
        self.assertEqual(comp_deleted.comment, None)
 | 
			
		||||
 | 
			
		||||
        intervention_last_log = self.compensation.intervention.log.first()
 | 
			
		||||
        self.assertEqual(intervention_last_log.action, UserAction.EDITED)
 | 
			
		||||
        self.assertEqual(intervention_last_log.user, self.superuser)
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            intervention_last_log.comment,
 | 
			
		||||
            COMPENSATION_REMOVED_TEMPLATE.format(
 | 
			
		||||
                self.compensation.identifier
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CompensationDocumentTestCase(BaseTestCase):
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
        self.doc = self.create_dummy_document(CompensationDocument, self.compensation)
 | 
			
		||||
 | 
			
		||||
    def test_delete(self):
 | 
			
		||||
        doc_title = self.doc.title
 | 
			
		||||
        self.assertIn(self.doc, self.compensation.get_documents())
 | 
			
		||||
        self.doc.delete(self.superuser)
 | 
			
		||||
        self.assertNotIn(self.doc, self.compensation.get_documents())
 | 
			
		||||
        try:
 | 
			
		||||
            self.doc.refresh_from_db()
 | 
			
		||||
            self.fail("Document should not be fetchable anymore")
 | 
			
		||||
        except ObjectDoesNotExist:
 | 
			
		||||
            pass
 | 
			
		||||
        last_log = self.compensation.log.first()
 | 
			
		||||
        self.assertEqual(last_log.user, self.superuser)
 | 
			
		||||
        self.assertEqual(last_log.action, UserAction.EDITED)
 | 
			
		||||
        self.assertEqual(last_log.comment, DOCUMENT_REMOVED_TEMPLATE.format(doc_title))
 | 
			
		||||
@ -106,14 +106,17 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase):
 | 
			
		||||
            "surface": test_deductable_surface,
 | 
			
		||||
            "conservation_office": test_conservation_office.id
 | 
			
		||||
        }
 | 
			
		||||
        self.client_user.post(url, post_data)
 | 
			
		||||
        response = self.client_user.post(url, post_data)
 | 
			
		||||
        self.assertEqual(response.status_code, 302, msg=f"{response.content.decode('utf-8')}")
 | 
			
		||||
        self.eco_account.refresh_from_db()
 | 
			
		||||
 | 
			
		||||
        deductions_surface = self.eco_account.get_deductions_surface()
 | 
			
		||||
 | 
			
		||||
        check_on_elements = {
 | 
			
		||||
            self.eco_account.title: new_title,
 | 
			
		||||
            self.eco_account.identifier: new_identifier,
 | 
			
		||||
            self.eco_account.deductable_surface: test_deductable_surface,
 | 
			
		||||
            self.eco_account.deductable_rest: test_deductable_surface,
 | 
			
		||||
            self.eco_account.deductable_rest: test_deductable_surface - deductions_surface,
 | 
			
		||||
            self.eco_account.comment: new_comment,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -223,7 +226,9 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase):
 | 
			
		||||
        self.eco_account.refresh_from_db()
 | 
			
		||||
        self.assertEqual(1, self.eco_account.deductions.count())
 | 
			
		||||
        self.assertEqual(1, self.intervention.deductions.count())
 | 
			
		||||
        deduction = self.eco_account.deductions.first()
 | 
			
		||||
        deduction = self.eco_account.deductions.get(
 | 
			
		||||
            surface=test_surface
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(deduction.surface, test_surface)
 | 
			
		||||
        self.assertEqual(self.eco_account.deductable_rest, self.eco_account.deductable_surface - deduction.surface)
 | 
			
		||||
        self.assertEqual(deduction.account, self.eco_account)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										7
									
								
								compensation/tests/ecoaccount/unit/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								compensation/tests/ecoaccount/unit/__init__.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: ksp-servicestelle@sgdnord.rlp.de
 | 
			
		||||
Created on: 30.08.23
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
							
								
								
									
										128
									
								
								compensation/tests/ecoaccount/unit/test_models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								compensation/tests/ecoaccount/unit/test_models.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,128 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: ksp-servicestelle@sgdnord.rlp.de
 | 
			
		||||
Created on: 30.08.23
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
from django.core.exceptions import ObjectDoesNotExist
 | 
			
		||||
from django.urls import reverse
 | 
			
		||||
 | 
			
		||||
from compensation.models import EcoAccountDocument
 | 
			
		||||
from konova.tests.test_views import BaseTestCase
 | 
			
		||||
from konova.utils.message_templates import DOCUMENT_REMOVED_TEMPLATE, DEDUCTION_REMOVED
 | 
			
		||||
from user.models import UserAction
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EcoAccountTestCase(BaseTestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
 | 
			
		||||
    def test_str(self):
 | 
			
		||||
        self.assertEqual(str(self.eco_account), f"{self.eco_account.identifier} ({self.eco_account.title})")
 | 
			
		||||
 | 
			
		||||
    def test_save(self):
 | 
			
		||||
        old_id = self.eco_account.identifier
 | 
			
		||||
        self.assertIsNotNone(self.eco_account.identifier)
 | 
			
		||||
        self.eco_account.identifier = None
 | 
			
		||||
        self.eco_account.save()
 | 
			
		||||
        self.assertIsNotNone(self.eco_account.identifier)
 | 
			
		||||
        self.assertNotEqual(old_id, self.eco_account.identifier)
 | 
			
		||||
 | 
			
		||||
    def test_property_deductions_surface_sum(self):
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            self.eco_account.deductions_surface_sum,
 | 
			
		||||
            self.eco_account.get_deductions_surface()
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_get_documents(self):
 | 
			
		||||
        docs = self.eco_account.get_documents()
 | 
			
		||||
        self.assertEqual(docs.count(), 0)
 | 
			
		||||
        doc = self.create_dummy_document(EcoAccountDocument, self.eco_account)
 | 
			
		||||
        self.assertIn(doc, self.eco_account.get_documents())
 | 
			
		||||
 | 
			
		||||
    def test_get_share_link(self):
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            self.eco_account.get_share_link(),
 | 
			
		||||
            reverse(
 | 
			
		||||
                "compensation:acc:share-token",
 | 
			
		||||
                args=(self.eco_account.id, self.eco_account.access_token)
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_get_deductable_rest_relative(self):
 | 
			
		||||
        self.assertEqual(self.eco_account.deductions.count(), 0)
 | 
			
		||||
        self.eco_account.deductable_surface = 5.0
 | 
			
		||||
        self.eco_account.save()
 | 
			
		||||
        self.eco_account.update_deductable_rest()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(self.eco_account.get_deductable_rest_relative(), 100)
 | 
			
		||||
        self.eco_account.deductable_surface = None
 | 
			
		||||
        self.eco_account.save()
 | 
			
		||||
        self.assertEqual(self.eco_account.get_deductable_rest_relative(), 0)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EcoAccountDocumentTestCase(BaseTestCase):
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
 | 
			
		||||
    def test_delete(self):
 | 
			
		||||
        doc = self.create_dummy_document(
 | 
			
		||||
            EcoAccountDocument,
 | 
			
		||||
            self.eco_account
 | 
			
		||||
        )
 | 
			
		||||
        doc_title = doc.title
 | 
			
		||||
        docs = self.eco_account.get_documents()
 | 
			
		||||
        self.assertIn(doc, docs)
 | 
			
		||||
 | 
			
		||||
        doc.delete(user=self.superuser)
 | 
			
		||||
        last_log = self.eco_account.log.first()
 | 
			
		||||
        self.assertEqual(last_log.user, self.superuser)
 | 
			
		||||
        self.assertEqual(last_log.action, UserAction.EDITED)
 | 
			
		||||
        self.assertEqual(last_log.comment, DOCUMENT_REMOVED_TEMPLATE.format(
 | 
			
		||||
            doc_title
 | 
			
		||||
        ))
 | 
			
		||||
        try:
 | 
			
		||||
            doc.refresh_from_db()
 | 
			
		||||
            self.fail("Document should not have been fetchable")
 | 
			
		||||
        except ObjectDoesNotExist:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EcoAccountDeductionTestCase(BaseTestCase):
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
 | 
			
		||||
    def test_str(self):
 | 
			
		||||
        self.assertEqual(str(self.deduction), f"{self.deduction.surface} of {self.deduction.account}")
 | 
			
		||||
 | 
			
		||||
    def test_delete(self):
 | 
			
		||||
        self.deduction.account = self.eco_account
 | 
			
		||||
        self.deduction.intervention = self.intervention
 | 
			
		||||
        self.deduction.save()
 | 
			
		||||
 | 
			
		||||
        self.eco_account.update_deductable_rest()
 | 
			
		||||
        old_deductable_rest = self.eco_account.deductable_rest
 | 
			
		||||
        deduction_surface = self.deduction.surface
 | 
			
		||||
 | 
			
		||||
        self.deduction.delete(self.superuser)
 | 
			
		||||
 | 
			
		||||
        last_log_intervention = self.intervention.log.first()
 | 
			
		||||
        last_log_account = self.eco_account.log.first()
 | 
			
		||||
        logs = [
 | 
			
		||||
            last_log_intervention,
 | 
			
		||||
            last_log_account,
 | 
			
		||||
        ]
 | 
			
		||||
        for log in logs:
 | 
			
		||||
            self.assertEqual(log.action, UserAction.EDITED)
 | 
			
		||||
            self.assertEqual(log.user, self.superuser)
 | 
			
		||||
            self.assertEqual(log.comment, DEDUCTION_REMOVED)
 | 
			
		||||
 | 
			
		||||
        self.assertLess(old_deductable_rest, self.eco_account.deductable_rest)
 | 
			
		||||
        self.assertEqual(old_deductable_rest + deduction_surface, self.eco_account.deductable_rest)
 | 
			
		||||
        try:
 | 
			
		||||
            self.deduction.refresh_from_db()
 | 
			
		||||
            self.fail("Deduction still fetchable after deleting")
 | 
			
		||||
        except ObjectDoesNotExist:
 | 
			
		||||
            pass
 | 
			
		||||
@ -25,9 +25,8 @@ from konova.forms.modals import RemoveModalForm
 | 
			
		||||
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
 | 
			
		||||
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
 | 
			
		||||
from konova.utils.message_templates import COMPENSATION_REMOVED_TEMPLATE, DATA_CHECKED_PREVIOUSLY_TEMPLATE, \
 | 
			
		||||
    RECORDED_BLOCKS_EDIT, CHECKED_RECORDED_RESET, FORM_INVALID, PARAMS_INVALID, IDENTIFIER_REPLACED, \
 | 
			
		||||
    RECORDED_BLOCKS_EDIT, CHECK_STATE_RESET, FORM_INVALID, PARAMS_INVALID, IDENTIFIER_REPLACED, \
 | 
			
		||||
    COMPENSATION_ADDED_TEMPLATE, DO_NOT_FORGET_TO_SHARE, GEOMETRY_SIMPLIFIED
 | 
			
		||||
from konova.utils.user_checks import in_group
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@login_required
 | 
			
		||||
@ -170,15 +169,14 @@ def edit_view(request: HttpRequest, id: str):
 | 
			
		||||
    geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=comp)
 | 
			
		||||
    if request.method == "POST":
 | 
			
		||||
        if data_form.is_valid() and geom_form.is_valid():
 | 
			
		||||
            # Preserve state of intervention recorded/checked to determine whether the user must be informed or not
 | 
			
		||||
            # about a change of the recorded/checked state
 | 
			
		||||
            intervention_recorded = comp.intervention.recorded is not None
 | 
			
		||||
            intervention_checked = comp.intervention.checked is not None
 | 
			
		||||
            # Preserve state of intervention checked to determine whether the user must be informed or not
 | 
			
		||||
            # about a change of the check state
 | 
			
		||||
            intervention_is_checked = comp.intervention.checked is not None
 | 
			
		||||
 | 
			
		||||
            # The data form takes the geom form for processing, as well as the performing user
 | 
			
		||||
            comp = data_form.save(request.user, geom_form)
 | 
			
		||||
            if intervention_recorded or intervention_checked:
 | 
			
		||||
                messages.info(request, CHECKED_RECORDED_RESET)
 | 
			
		||||
            if intervention_is_checked:
 | 
			
		||||
                messages.info(request, CHECK_STATE_RESET)
 | 
			
		||||
            messages.success(request, _("Compensation {} edited").format(comp.identifier))
 | 
			
		||||
            if geom_form.geometry_simplified:
 | 
			
		||||
                messages.info(
 | 
			
		||||
@ -266,9 +264,9 @@ def detail_view(request: HttpRequest, id: str):
 | 
			
		||||
        "sum_before_states": sum_before_states,
 | 
			
		||||
        "sum_after_states": sum_after_states,
 | 
			
		||||
        "diff_states": diff_states,
 | 
			
		||||
        "is_default_member": in_group(_user, DEFAULT_GROUP),
 | 
			
		||||
        "is_zb_member": in_group(_user, ZB_GROUP),
 | 
			
		||||
        "is_ets_member": in_group(_user, ETS_GROUP),
 | 
			
		||||
        "is_default_member": _user.in_group(DEFAULT_GROUP),
 | 
			
		||||
        "is_zb_member": _user.in_group(ZB_GROUP),
 | 
			
		||||
        "is_ets_member": _user.in_group(ETS_GROUP),
 | 
			
		||||
        "LANIS_LINK": comp.get_LANIS_link(),
 | 
			
		||||
        TAB_TITLE_IDENTIFIER: f"{comp.identifier} - {comp.title}",
 | 
			
		||||
        "has_finished_deadlines": comp.get_finished_deadlines().exists(),
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,6 @@ from konova.settings import ETS_GROUP, DEFAULT_GROUP, ZB_GROUP
 | 
			
		||||
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
 | 
			
		||||
from konova.utils.message_templates import CANCEL_ACC_RECORDED_OR_DEDUCTED, RECORDED_BLOCKS_EDIT, FORM_INVALID, \
 | 
			
		||||
    IDENTIFIER_REPLACED, DO_NOT_FORGET_TO_SHARE, GEOMETRY_SIMPLIFIED
 | 
			
		||||
from konova.utils.user_checks import in_group
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@login_required
 | 
			
		||||
@ -150,7 +149,9 @@ def edit_view(request: HttpRequest, id: str):
 | 
			
		||||
    data_form = EditEcoAccountForm(request.POST or None, instance=acc)
 | 
			
		||||
    geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=acc)
 | 
			
		||||
    if request.method == "POST":
 | 
			
		||||
        if data_form.is_valid() and geom_form.is_valid():
 | 
			
		||||
        data_form_valid = data_form.is_valid()
 | 
			
		||||
        geom_form_valid = geom_form.is_valid()
 | 
			
		||||
        if data_form_valid and geom_form_valid:
 | 
			
		||||
            # The data form takes the geom form for processing, as well as the performing user
 | 
			
		||||
            acc = data_form.save(request.user, geom_form)
 | 
			
		||||
            messages.success(request, _("Eco-Account {} edited").format(acc.identifier))
 | 
			
		||||
@ -242,9 +243,9 @@ def detail_view(request: HttpRequest, id: str):
 | 
			
		||||
        "diff_states": diff_states,
 | 
			
		||||
        "available": available_relative,
 | 
			
		||||
        "available_total": available_total,
 | 
			
		||||
        "is_default_member": in_group(_user, DEFAULT_GROUP),
 | 
			
		||||
        "is_zb_member": in_group(_user, ZB_GROUP),
 | 
			
		||||
        "is_ets_member": in_group(_user, ETS_GROUP),
 | 
			
		||||
        "is_default_member": _user.in_group(DEFAULT_GROUP),
 | 
			
		||||
        "is_zb_member": _user.in_group(ZB_GROUP),
 | 
			
		||||
        "is_ets_member": _user.in_group(ETS_GROUP),
 | 
			
		||||
        "LANIS_LINK": acc.get_LANIS_link(),
 | 
			
		||||
        "deductions": deductions,
 | 
			
		||||
        "actions": actions,
 | 
			
		||||
@ -275,7 +276,7 @@ def remove_view(request: HttpRequest, id: str):
 | 
			
		||||
    # default group user
 | 
			
		||||
    if acc.recorded is not None or acc.deductions.exists():
 | 
			
		||||
        user = request.user
 | 
			
		||||
        if not in_group(user, ETS_GROUP):
 | 
			
		||||
        if not user.in_group(ETS_GROUP):
 | 
			
		||||
            messages.info(request, CANCEL_ACC_RECORDED_OR_DEDUCTED)
 | 
			
		||||
            return redirect("compensation:acc:detail", id=id)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										12
									
								
								ema/forms.py
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								ema/forms.py
									
									
									
									
									
								
							@ -76,7 +76,7 @@ class NewEmaForm(AbstractCompensationForm, CompensationResponsibleFormMixin, Pik
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            # Finally create main object
 | 
			
		||||
            acc = Ema.objects.create(
 | 
			
		||||
            ema = Ema.objects.create(
 | 
			
		||||
                identifier=identifier,
 | 
			
		||||
                title=title,
 | 
			
		||||
                responsible=responsible,
 | 
			
		||||
@ -87,16 +87,16 @@ class NewEmaForm(AbstractCompensationForm, CompensationResponsibleFormMixin, Pik
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            # Add the creating user to the list of shared users
 | 
			
		||||
            acc.share_with_user(user)
 | 
			
		||||
            ema.share_with_user(user)
 | 
			
		||||
 | 
			
		||||
            # Add the log entry to the main objects log list
 | 
			
		||||
            acc.log.add(action)
 | 
			
		||||
            ema.log.add(action)
 | 
			
		||||
 | 
			
		||||
        # Process the geometry form (NOT ATOMIC TRANSACTION DUE TO CELERY!)
 | 
			
		||||
        geometry = geom_form.save(action)
 | 
			
		||||
        acc.geometry = geometry
 | 
			
		||||
        acc.save()
 | 
			
		||||
        return acc
 | 
			
		||||
        ema.geometry = geometry
 | 
			
		||||
        ema.save()
 | 
			
		||||
        return ema
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EditEmaForm(NewEmaForm):
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,7 @@ from django.urls import reverse
 | 
			
		||||
 | 
			
		||||
from compensation.models import AbstractCompensation, PikMixin
 | 
			
		||||
from ema.managers import EmaManager
 | 
			
		||||
from ema.settings import EMA_IDENTIFIER_LENGTH, EMA_IDENTIFIER_TEMPLATE
 | 
			
		||||
from ema.utils.quality import EmaQualityChecker
 | 
			
		||||
from konova.models import AbstractDocument, generate_document_file_upload_path, RecordableObjectMixin, ShareableObjectMixin
 | 
			
		||||
from konova.utils.message_templates import DATA_UNSHARED_EXPLANATION, DOCUMENT_REMOVED_TEMPLATE
 | 
			
		||||
@ -38,6 +39,9 @@ class Ema(AbstractCompensation, ShareableObjectMixin, RecordableObjectMixin, Pik
 | 
			
		||||
    """
 | 
			
		||||
    objects = EmaManager()
 | 
			
		||||
 | 
			
		||||
    identifier_length = EMA_IDENTIFIER_LENGTH
 | 
			
		||||
    identifier_template = EMA_IDENTIFIER_TEMPLATE
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return "{}".format(self.identifier)
 | 
			
		||||
 | 
			
		||||
@ -122,7 +126,7 @@ class EmaDocument(AbstractDocument):
 | 
			
		||||
 | 
			
		||||
    def delete(self, user=None, *args, **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        Custom delete functionality for EcoAccountDocuments.
 | 
			
		||||
        Custom delete functionality for EmaDocuments.
 | 
			
		||||
        Removes the folder from the file system if there are no further documents for this entry.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
@ -139,8 +143,11 @@ class EmaDocument(AbstractDocument):
 | 
			
		||||
            # The only file left for this EMA is the one which is currently processed and will be deleted
 | 
			
		||||
            # Make sure that the compensation folder itself is deleted as well, not only the file
 | 
			
		||||
            # Therefore take the folder path from the file path
 | 
			
		||||
            folder_path = self.file.path.split("/")[:-1]
 | 
			
		||||
            folder_path = "/".join(folder_path)
 | 
			
		||||
            try:
 | 
			
		||||
                folder_path = self.file.path.split("/")[:-1]
 | 
			
		||||
                folder_path = "/".join(folder_path)
 | 
			
		||||
            except ValueError:
 | 
			
		||||
                folder_path = None
 | 
			
		||||
 | 
			
		||||
        if user:
 | 
			
		||||
            self.instance.mark_as_edited(user, edit_comment=DOCUMENT_REMOVED_TEMPLATE.format(self.title))
 | 
			
		||||
 | 
			
		||||
@ -6,5 +6,5 @@ Created on: 19.08.21
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
EMA_ACCOUNT_IDENTIFIER_LENGTH = 6
 | 
			
		||||
EMA_ACCOUNT_IDENTIFIER_TEMPLATE = "EMA-{}"
 | 
			
		||||
EMA_IDENTIFIER_LENGTH = 6
 | 
			
		||||
EMA_IDENTIFIER_TEMPLATE = "EMA-{}"
 | 
			
		||||
							
								
								
									
										7
									
								
								ema/tests/unit/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								ema/tests/unit/__init__.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: ksp-servicestelle@sgdnord.rlp.de
 | 
			
		||||
Created on: 24.08.23
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
							
								
								
									
										141
									
								
								ema/tests/unit/test_forms.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								ema/tests/unit/test_forms.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,141 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: ksp-servicestelle@sgdnord.rlp.de
 | 
			
		||||
Created on: 01.09.23
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
import json
 | 
			
		||||
 | 
			
		||||
from django.urls import reverse
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from ema.forms import NewEmaForm, EditEmaForm
 | 
			
		||||
from konova.forms import SimpleGeomForm
 | 
			
		||||
from konova.tests.test_views import BaseTestCase
 | 
			
		||||
from konova.utils.generators import generate_random_string
 | 
			
		||||
from user.models import UserAction
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NewEmaFormTestCase(BaseTestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
 | 
			
		||||
    def test_init(self):
 | 
			
		||||
        form = NewEmaForm()
 | 
			
		||||
        self.assertEqual(form.form_title, str(_("New EMA")))
 | 
			
		||||
        self.assertEqual(form.action_url, reverse("ema:new"))
 | 
			
		||||
        self.assertEqual(form.cancel_redirect, reverse("ema:index"))
 | 
			
		||||
        self.assertIsNotNone(form.fields["identifier"].initial)
 | 
			
		||||
        self.assertEqual(form.fields["title"].widget.attrs["placeholder"], str(_("Compensation XY; Location ABC")))
 | 
			
		||||
 | 
			
		||||
    def test_save(self):
 | 
			
		||||
        cons_office_code = self.get_conservation_office_code()
 | 
			
		||||
        data = {
 | 
			
		||||
            "identifier": generate_random_string(length=20, use_numbers=True),
 | 
			
		||||
            "title": generate_random_string(length=20, use_letters_lc=True),
 | 
			
		||||
            "conservation_office": cons_office_code,
 | 
			
		||||
            "conservation_file_number": generate_random_string(length=10, use_numbers=True),
 | 
			
		||||
            "is_pik": True,
 | 
			
		||||
            "comment": generate_random_string(length=20, use_numbers=True, use_letters_uc=True),
 | 
			
		||||
        }
 | 
			
		||||
        form = NewEmaForm(data)
 | 
			
		||||
 | 
			
		||||
        test_geom = self.create_dummy_geometry()
 | 
			
		||||
        geom_form_data = self.create_geojson(
 | 
			
		||||
            test_geom
 | 
			
		||||
        )
 | 
			
		||||
        geom_form_data = json.loads(geom_form_data)
 | 
			
		||||
        geom_form_data = {
 | 
			
		||||
            "geom": json.dumps(geom_form_data)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        geom_form = SimpleGeomForm(geom_form_data)
 | 
			
		||||
        self.assertTrue(form.is_valid(), msg=form.errors)
 | 
			
		||||
        self.assertTrue(geom_form.is_valid(), msg=form.errors)
 | 
			
		||||
 | 
			
		||||
        obj = form.save(user=self.superuser, geom_form=geom_form)
 | 
			
		||||
        self.assertEqual(obj.title, data["title"])
 | 
			
		||||
        self.assertEqual(obj.is_pik, data["is_pik"])
 | 
			
		||||
        self.assertIsNotNone(obj.responsible)
 | 
			
		||||
        self.assertIsNotNone(obj.responsible.handler)
 | 
			
		||||
        self.assertEqual(obj.responsible.conservation_office, data["conservation_office"])
 | 
			
		||||
        self.assertEqual(obj.responsible.conservation_file_number, data["conservation_file_number"])
 | 
			
		||||
        self.assertEqual(obj.identifier, data["identifier"])
 | 
			
		||||
        self.assertEqual(obj.comment, data["comment"])
 | 
			
		||||
 | 
			
		||||
        self.assertIn(self.superuser, obj.shared_users)
 | 
			
		||||
 | 
			
		||||
        last_log = obj.log.first()
 | 
			
		||||
        self.assertEqual(obj.created, obj.modified)
 | 
			
		||||
        self.assertEqual(obj.created, last_log)
 | 
			
		||||
        self.assertEqual(last_log.action, UserAction.CREATED)
 | 
			
		||||
        self.assertEqual(last_log.user, self.superuser)
 | 
			
		||||
        self.assertTrue(test_geom.equals_exact(obj.geometry.geom, 0.000001))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EditEmaFormTestCase(BaseTestCase):
 | 
			
		||||
    def test_init(self):
 | 
			
		||||
        form = EditEmaForm(instance=self.ema)
 | 
			
		||||
        self.assertEqual(form.form_title, str(_("Edit EMA")))
 | 
			
		||||
        self.assertEqual(form.action_url, reverse("ema:edit", args=(self.ema.id,)))
 | 
			
		||||
        self.assertEqual(form.cancel_redirect, reverse("ema:detail", args=(self.ema.id,)))
 | 
			
		||||
        self.assertEqual(form.fields["identifier"].widget.attrs["url"], reverse("ema:new-id"))
 | 
			
		||||
        self.assertEqual(form.fields["title"].widget.attrs["placeholder"], str(_("Compensation XY; Location ABC")))
 | 
			
		||||
 | 
			
		||||
        values = {
 | 
			
		||||
            "identifier": self.ema.identifier,
 | 
			
		||||
            "title": self.ema.title,
 | 
			
		||||
            "comment": self.ema.comment,
 | 
			
		||||
            "conservation_office": self.ema.responsible.conservation_office,
 | 
			
		||||
            "conservation_file_number": self.ema.responsible.conservation_file_number,
 | 
			
		||||
            "is_pik": self.ema.is_pik,
 | 
			
		||||
            "handler_type": self.ema.responsible.handler.type,
 | 
			
		||||
            "handler_detail": self.ema.responsible.handler.detail,
 | 
			
		||||
        }
 | 
			
		||||
        for k, v in values.items():
 | 
			
		||||
            self.assertEqual(form.fields[k].initial, v)
 | 
			
		||||
 | 
			
		||||
    def test_save(self):
 | 
			
		||||
        cons_office_code = self.get_conservation_office_code()
 | 
			
		||||
        data = {
 | 
			
		||||
            "identifier": generate_random_string(length=20, use_numbers=True),
 | 
			
		||||
            "title": generate_random_string(length=20, use_letters_lc=True),
 | 
			
		||||
            "conservation_office": cons_office_code,
 | 
			
		||||
            "conservation_file_number": generate_random_string(length=10, use_numbers=True),
 | 
			
		||||
            "is_pik": not self.ema.is_pik,
 | 
			
		||||
            "comment": generate_random_string(length=20, use_numbers=True, use_letters_uc=True),
 | 
			
		||||
        }
 | 
			
		||||
        form = EditEmaForm(data, instance=self.ema)
 | 
			
		||||
        self.assertTrue(form.is_valid(), msg=form.errors)
 | 
			
		||||
 | 
			
		||||
        test_geom = self.create_dummy_geometry()
 | 
			
		||||
        geom_form_data = self.create_geojson(
 | 
			
		||||
            test_geom
 | 
			
		||||
        )
 | 
			
		||||
        geom_form_data = json.loads(geom_form_data)
 | 
			
		||||
        geom_form_data = {
 | 
			
		||||
            "geom": json.dumps(geom_form_data)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        geom_form = SimpleGeomForm(geom_form_data)
 | 
			
		||||
        self.assertTrue(geom_form.is_valid())
 | 
			
		||||
 | 
			
		||||
        obj = form.save(self.superuser, geom_form)
 | 
			
		||||
        self.assertEqual(obj.id, self.ema.id)
 | 
			
		||||
        self.assertEqual(obj.title, data["title"])
 | 
			
		||||
        self.assertEqual(obj.is_pik, data["is_pik"])
 | 
			
		||||
        self.assertIsNotNone(obj.responsible)
 | 
			
		||||
        self.assertIsNotNone(obj.responsible.handler)
 | 
			
		||||
        self.assertEqual(obj.responsible.conservation_office, data["conservation_office"])
 | 
			
		||||
        self.assertEqual(obj.responsible.conservation_file_number, data["conservation_file_number"])
 | 
			
		||||
        self.assertEqual(obj.identifier, data["identifier"])
 | 
			
		||||
        self.assertEqual(obj.comment, data["comment"])
 | 
			
		||||
 | 
			
		||||
        last_log = obj.log.first()
 | 
			
		||||
        self.assertEqual(obj.modified, last_log)
 | 
			
		||||
        self.assertEqual(last_log.action, UserAction.EDITED)
 | 
			
		||||
        self.assertEqual(last_log.user, self.superuser)
 | 
			
		||||
        self.assertTrue(test_geom.equals_exact(obj.geometry.geom, 0.000001))
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										90
									
								
								ema/tests/unit/test_models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								ema/tests/unit/test_models.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,90 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: ksp-servicestelle@sgdnord.rlp.de
 | 
			
		||||
Created on: 24.08.23
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
from django.urls import reverse
 | 
			
		||||
from django.utils.timezone import now
 | 
			
		||||
 | 
			
		||||
from ema.models import Ema, EmaDocument
 | 
			
		||||
from ema.settings import EMA_IDENTIFIER_TEMPLATE
 | 
			
		||||
from konova.tests.test_views import BaseTestCase
 | 
			
		||||
from konova.utils.message_templates import DOCUMENT_REMOVED_TEMPLATE
 | 
			
		||||
from user.models import UserAction
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EmaModelTestCase(BaseTestCase):
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
 | 
			
		||||
    def test_str(self):
 | 
			
		||||
        self.assertEqual(str(self.ema), f"{self.ema.identifier}")
 | 
			
		||||
 | 
			
		||||
    def test_save(self):
 | 
			
		||||
        new_ema = Ema(
 | 
			
		||||
            title="Test"
 | 
			
		||||
        )
 | 
			
		||||
        self.assertIsNone(new_ema.identifier)
 | 
			
		||||
 | 
			
		||||
        new_ema.save()
 | 
			
		||||
        new_ema.refresh_from_db()
 | 
			
		||||
 | 
			
		||||
        self.assertIsNotNone(new_ema.identifier)
 | 
			
		||||
        self.assertIn("EMA-", new_ema.identifier)
 | 
			
		||||
 | 
			
		||||
    def test_is_ready_for_publish(self):
 | 
			
		||||
        self.assertIsNone(self.ema.recorded)
 | 
			
		||||
        self.assertFalse(self.ema.is_ready_for_publish())
 | 
			
		||||
 | 
			
		||||
        self.ema.set_recorded(self.superuser)
 | 
			
		||||
        self.ema.refresh_from_db()
 | 
			
		||||
        self.assertIsNotNone(self.ema.recorded)
 | 
			
		||||
        self.assertTrue(self.ema.is_ready_for_publish())
 | 
			
		||||
 | 
			
		||||
    def test_get_share_link(self):
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            self.ema.get_share_link(),
 | 
			
		||||
            reverse("ema:share-token", args=(self.ema.id, self.ema.access_token))
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_get_documents(self):
 | 
			
		||||
        self.assertEqual(self.ema.get_documents().count(), 0)
 | 
			
		||||
 | 
			
		||||
        doc = EmaDocument(
 | 
			
		||||
            instance=self.ema,
 | 
			
		||||
            date_of_creation=now().date(),
 | 
			
		||||
            comment="Test",
 | 
			
		||||
        )
 | 
			
		||||
        doc.save()
 | 
			
		||||
        docs = self.ema.get_documents()
 | 
			
		||||
        self.assertEqual(docs.count(), 1)
 | 
			
		||||
        self.assertEqual(docs.first(), doc)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EmaDocumentModelTestCase(BaseTestCase):
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
 | 
			
		||||
    def test_delete(self):
 | 
			
		||||
        doc = EmaDocument.objects.create(
 | 
			
		||||
            date_of_creation=now().date(),
 | 
			
		||||
            instance=self.ema,
 | 
			
		||||
            comment="TEST"
 | 
			
		||||
        )
 | 
			
		||||
        self.ema.refresh_from_db()
 | 
			
		||||
        docs = self.ema.get_documents()
 | 
			
		||||
        self.assertEqual(docs.count(), 1)
 | 
			
		||||
        self.assertEqual(docs.first(), doc)
 | 
			
		||||
 | 
			
		||||
        doc_title = doc.title
 | 
			
		||||
        doc.delete(user=self.superuser)
 | 
			
		||||
        last_log = self.ema.log.first()
 | 
			
		||||
        self.assertEqual(last_log.action, UserAction.EDITED)
 | 
			
		||||
        self.assertEqual(last_log.user, self.superuser)
 | 
			
		||||
        self.assertEqual(last_log.comment, DOCUMENT_REMOVED_TEMPLATE.format(doc_title))
 | 
			
		||||
 | 
			
		||||
        docs = self.ema.get_documents()
 | 
			
		||||
        self.assertEqual(docs.count(), 0)
 | 
			
		||||
 | 
			
		||||
@ -24,7 +24,6 @@ from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
 | 
			
		||||
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
 | 
			
		||||
from konova.utils.message_templates import RECORDED_BLOCKS_EDIT, IDENTIFIER_REPLACED, FORM_INVALID, \
 | 
			
		||||
    DO_NOT_FORGET_TO_SHARE, GEOMETRY_SIMPLIFIED
 | 
			
		||||
from konova.utils.user_checks import in_group
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@login_required
 | 
			
		||||
@ -172,9 +171,9 @@ def detail_view(request: HttpRequest, id: str):
 | 
			
		||||
        "sum_before_states": sum_before_states,
 | 
			
		||||
        "sum_after_states": sum_after_states,
 | 
			
		||||
        "diff_states": diff_states,
 | 
			
		||||
        "is_default_member": in_group(_user, DEFAULT_GROUP),
 | 
			
		||||
        "is_zb_member": in_group(_user, ZB_GROUP),
 | 
			
		||||
        "is_ets_member": in_group(_user, ETS_GROUP),
 | 
			
		||||
        "is_default_member": _user.in_group(DEFAULT_GROUP),
 | 
			
		||||
        "is_zb_member": _user.in_group(ZB_GROUP),
 | 
			
		||||
        "is_ets_member": _user.in_group(ETS_GROUP),
 | 
			
		||||
        "LANIS_LINK": ema.get_LANIS_link(),
 | 
			
		||||
        TAB_TITLE_IDENTIFIER: f"{ema.identifier} - {ema.title}",
 | 
			
		||||
        "has_finished_deadlines": ema.get_finished_deadlines().exists(),
 | 
			
		||||
 | 
			
		||||
@ -43,6 +43,7 @@ class CheckModalForm(BaseModalForm):
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        deductions = self.instance.deductions.all()
 | 
			
		||||
        valid = True
 | 
			
		||||
        for deduction in deductions:
 | 
			
		||||
            checker = deduction.account.quality_check()
 | 
			
		||||
            for msg in checker.messages:
 | 
			
		||||
@ -50,8 +51,8 @@ class CheckModalForm(BaseModalForm):
 | 
			
		||||
                    "checked_comps",
 | 
			
		||||
                    f"{deduction.account.identifier}: {msg}"
 | 
			
		||||
                )
 | 
			
		||||
            return checker.valid
 | 
			
		||||
        return True
 | 
			
		||||
            valid &= checker.valid
 | 
			
		||||
        return valid
 | 
			
		||||
 | 
			
		||||
    def _are_comps_valid(self):
 | 
			
		||||
        """ Performs validity checks on all types of compensations
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,6 @@ from django.utils.translation import gettext_lazy as _
 | 
			
		||||
from intervention.inputs import TextToClipboardInput
 | 
			
		||||
from konova.forms.modals import BaseModalForm
 | 
			
		||||
from konova.utils.message_templates import ENTRY_REMOVE_MISSING_PERMISSION
 | 
			
		||||
from konova.utils.user_checks import is_default_group_only
 | 
			
		||||
from user.models import Team, User
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -80,7 +79,7 @@ class ShareModalForm(BaseModalForm):
 | 
			
		||||
        teams = self.cleaned_data.get("teams", Team.objects.none())
 | 
			
		||||
 | 
			
		||||
        _is_valid = True
 | 
			
		||||
        if is_default_group_only(self.user):
 | 
			
		||||
        if self.user.is_default_group_only():
 | 
			
		||||
            shared_users = self.instance.shared_users
 | 
			
		||||
            shared_teams = self.instance.shared_teams
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,7 @@ from django.urls import reverse
 | 
			
		||||
from django.utils import timezone
 | 
			
		||||
 | 
			
		||||
from analysis.settings import LKOMPVZVO_PUBLISH_DATE
 | 
			
		||||
from compensation.models import EcoAccountDeduction
 | 
			
		||||
from intervention.settings import INTERVENTION_IDENTIFIER_LENGTH, INTERVENTION_IDENTIFIER_TEMPLATE
 | 
			
		||||
from intervention.tasks import celery_export_to_egon
 | 
			
		||||
from user.models import User
 | 
			
		||||
from django.db import models, transaction
 | 
			
		||||
@ -61,6 +61,9 @@ class Intervention(BaseObject,
 | 
			
		||||
 | 
			
		||||
    objects = InterventionManager()
 | 
			
		||||
 | 
			
		||||
    identifier_length = INTERVENTION_IDENTIFIER_LENGTH
 | 
			
		||||
    identifier_template = INTERVENTION_IDENTIFIER_TEMPLATE
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return f"{self.identifier} ({self.title})"
 | 
			
		||||
 | 
			
		||||
@ -276,22 +279,20 @@ class Intervention(BaseObject,
 | 
			
		||||
            revocation.delete()
 | 
			
		||||
            self.mark_as_edited(user, request=form.request, edit_comment=REVOCATION_REMOVED)
 | 
			
		||||
 | 
			
		||||
    def mark_as_edited(self, performing_user: User, request: HttpRequest = None, edit_comment: str = None, reset_recorded: bool = True):
 | 
			
		||||
        """ In case the object or a related object changed, internal processes need to be started, such as
 | 
			
		||||
        unrecord and uncheck
 | 
			
		||||
    def mark_as_edited(self, performing_user: User, request: HttpRequest = None, edit_comment: str = None):
 | 
			
		||||
        """ Log the edit action
 | 
			
		||||
 | 
			
		||||
        If the object is checked, set it to unchecked due to the editing. Another check is needed then.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            performing_user (User): The user which performed the editing action
 | 
			
		||||
            request (HttpRequest): The used request for this action
 | 
			
		||||
            edit_comment (str): Additional comment for the log entry
 | 
			
		||||
            reset_recorded (bool): Whether the record-state of the object should be reset
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        action = super().mark_as_edited(performing_user, edit_comment=edit_comment)
 | 
			
		||||
        if reset_recorded:
 | 
			
		||||
            self.unrecord(performing_user, request)
 | 
			
		||||
        if self.checked:
 | 
			
		||||
            self.set_unchecked()
 | 
			
		||||
        return action
 | 
			
		||||
@ -416,8 +417,11 @@ class InterventionDocument(AbstractDocument):
 | 
			
		||||
            # The only file left for this intervention is the one which is currently processed and will be deleted
 | 
			
		||||
            # Make sure that the intervention folder itself is deleted as well, not only the file
 | 
			
		||||
            # Therefore take the folder path from the file path
 | 
			
		||||
            folder_path = self.file.path.split("/")[:-1]
 | 
			
		||||
            folder_path = "/".join(folder_path)
 | 
			
		||||
            try:
 | 
			
		||||
                folder_path = self.file.path.split("/")[:-1]
 | 
			
		||||
                folder_path = "/".join(folder_path)
 | 
			
		||||
            except ValueError:
 | 
			
		||||
                folder_path = None
 | 
			
		||||
 | 
			
		||||
        if user:
 | 
			
		||||
            self.instance.mark_as_edited(user, edit_comment=DOCUMENT_REMOVED_TEMPLATE.format(self.title))
 | 
			
		||||
 | 
			
		||||
@ -400,12 +400,13 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase):
 | 
			
		||||
        self.eco_account.share_with_user_list([self.superuser])
 | 
			
		||||
        self.eco_account.save()
 | 
			
		||||
        num_all_deducs = EcoAccountDeduction.objects.count()
 | 
			
		||||
        num_acc_deducs = self.eco_account.deductions.count()
 | 
			
		||||
 | 
			
		||||
        # Run the request
 | 
			
		||||
        self.client_user.post(new_url, post_data)
 | 
			
		||||
 | 
			
		||||
        # Expect the deduction to be created, since all constraints are fulfilled
 | 
			
		||||
        self.assertEqual(1, self.eco_account.deductions.count())
 | 
			
		||||
        self.assertEqual(num_acc_deducs + 1, self.eco_account.deductions.count())
 | 
			
		||||
        self.assertEqual(num_all_deducs + 1, EcoAccountDeduction.objects.count())
 | 
			
		||||
 | 
			
		||||
        # Make sure the deduction contains the expected data
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										7
									
								
								intervention/tests/unit/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								intervention/tests/unit/__init__.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: ksp-servicestelle@sgdnord.rlp.de
 | 
			
		||||
Created on: 24.08.23
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
							
								
								
									
										333
									
								
								intervention/tests/unit/test_forms.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										333
									
								
								intervention/tests/unit/test_forms.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,333 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: ksp-servicestelle@sgdnord.rlp.de
 | 
			
		||||
Created on: 24.08.23
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
import json
 | 
			
		||||
from datetime import timedelta
 | 
			
		||||
 | 
			
		||||
from django.core.exceptions import ObjectDoesNotExist
 | 
			
		||||
from django.urls import reverse
 | 
			
		||||
from django.utils.timezone import now
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
from django.test import RequestFactory
 | 
			
		||||
 | 
			
		||||
from intervention.forms.intervention import NewInterventionForm, EditInterventionForm
 | 
			
		||||
from intervention.forms.modals.revocation import NewRevocationModalForm, EditRevocationModalForm, \
 | 
			
		||||
    RemoveRevocationModalForm
 | 
			
		||||
from intervention.forms.modals.share import ShareModalForm
 | 
			
		||||
from intervention.models import Revocation
 | 
			
		||||
from konova.forms import SimpleGeomForm
 | 
			
		||||
from konova.settings import DEFAULT_GROUP, ZB_GROUP
 | 
			
		||||
from konova.tests.test_views import BaseTestCase
 | 
			
		||||
from konova.utils.generators import generate_random_string
 | 
			
		||||
from konova.utils.message_templates import REVOCATION_ADDED, REVOCATION_EDITED, REVOCATION_REMOVED
 | 
			
		||||
from user.models import UserAction
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NewInterventionFormTestCase(BaseTestCase):
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
 | 
			
		||||
    def test_init(self):
 | 
			
		||||
        form = NewInterventionForm()
 | 
			
		||||
        self.assertEqual(form.form_title, str(_("New intervention")))
 | 
			
		||||
        self.assertEqual(form.action_url, reverse("intervention:new"))
 | 
			
		||||
        self.assertEqual(form.cancel_redirect, reverse("intervention:index"))
 | 
			
		||||
 | 
			
		||||
        initial_identifier = form.fields["identifier"].initial
 | 
			
		||||
        self.assertIsNotNone(initial_identifier)
 | 
			
		||||
        self.assertIn("EIV-", initial_identifier)
 | 
			
		||||
 | 
			
		||||
    def test_is_valid(self):
 | 
			
		||||
        data = {
 | 
			
		||||
            "identifier": generate_random_string(length=15, use_letters_uc=True),
 | 
			
		||||
            "title": generate_random_string(length=15, use_letters_uc=True),
 | 
			
		||||
        }
 | 
			
		||||
        form = NewInterventionForm({})
 | 
			
		||||
        self.assertFalse(form.is_valid())
 | 
			
		||||
        form = NewInterventionForm(data)
 | 
			
		||||
        self.assertTrue(form.is_valid(), msg=form.errors)
 | 
			
		||||
 | 
			
		||||
    def test_save(self):
 | 
			
		||||
        data = {
 | 
			
		||||
            "identifier": generate_random_string(length=15, use_letters_uc=True),
 | 
			
		||||
            "title": generate_random_string(length=15, use_letters_uc=True),
 | 
			
		||||
        }
 | 
			
		||||
        test_geom = self.create_dummy_geometry()
 | 
			
		||||
        geom_form_data = self.create_geojson(
 | 
			
		||||
            test_geom
 | 
			
		||||
        )
 | 
			
		||||
        geom_form_data = json.loads(geom_form_data)
 | 
			
		||||
        geom_form_data = {
 | 
			
		||||
            "geom": json.dumps(geom_form_data)
 | 
			
		||||
        }
 | 
			
		||||
        geom_form = SimpleGeomForm(geom_form_data)
 | 
			
		||||
 | 
			
		||||
        form = NewInterventionForm(data)
 | 
			
		||||
        self.assertTrue(form.is_valid())
 | 
			
		||||
        self.assertTrue(geom_form.is_valid())
 | 
			
		||||
        obj = form.save(self.superuser, geom_form)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(obj.identifier, data["identifier"])
 | 
			
		||||
        self.assertEqual(obj.title, data["title"])
 | 
			
		||||
        self.assertIsNotNone(obj.legal)
 | 
			
		||||
        self.assertIsNotNone(obj.responsible)
 | 
			
		||||
        self.assertIsNotNone(obj.responsible.handler)
 | 
			
		||||
        self.assertEqual(obj.created.action, UserAction.CREATED)
 | 
			
		||||
        self.assertEqual(obj.created.user, self.superuser)
 | 
			
		||||
        self.assertEqual(obj.created, obj.log.first())
 | 
			
		||||
        self.assertEqual(obj.created, obj.modified)
 | 
			
		||||
 | 
			
		||||
        self.assertIn(self.superuser, obj.shared_users)
 | 
			
		||||
        self.assertTrue(test_geom.equals_exact(obj.geometry.geom, 0.000001))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EditInterventionFormTestCase(NewInterventionFormTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_init(self):
 | 
			
		||||
        today = now().date()
 | 
			
		||||
        data = {
 | 
			
		||||
            "identifier": self.intervention.identifier,
 | 
			
		||||
            "title": generate_random_string(length=5, use_letters_lc=True),
 | 
			
		||||
            "comment": generate_random_string(length=5, use_letters_lc=True),
 | 
			
		||||
            "registration_date": today,
 | 
			
		||||
            "binding_date": today,
 | 
			
		||||
            "registration_file_number": generate_random_string(length=5, use_numbers=True),
 | 
			
		||||
            "conservation_file_number": generate_random_string(length=5, use_numbers=True),
 | 
			
		||||
        }
 | 
			
		||||
        test_geom = self.create_dummy_geometry()
 | 
			
		||||
        geom_form_data = self.create_geojson(
 | 
			
		||||
            test_geom
 | 
			
		||||
        )
 | 
			
		||||
        geom_form_data = json.loads(geom_form_data)
 | 
			
		||||
        geom_form_data = {
 | 
			
		||||
            "geom": json.dumps(geom_form_data)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        geom_form = SimpleGeomForm(geom_form_data)
 | 
			
		||||
        form = EditInterventionForm(data, instance=self.intervention)
 | 
			
		||||
        self.assertTrue(geom_form.is_valid())
 | 
			
		||||
        self.assertTrue(form.is_valid())
 | 
			
		||||
 | 
			
		||||
        obj = form.save(self.superuser, geom_form)
 | 
			
		||||
 | 
			
		||||
        last_log = obj.log.first()
 | 
			
		||||
        self.assertEqual(last_log.user, self.superuser)
 | 
			
		||||
        self.assertEqual(last_log.action, UserAction.EDITED)
 | 
			
		||||
        self.assertEqual(last_log, obj.modified)
 | 
			
		||||
        self.assertEqual(obj.identifier, self.intervention.identifier)
 | 
			
		||||
        self.assertIsNotNone(obj.legal)
 | 
			
		||||
        self.assertIsNotNone(obj.responsible)
 | 
			
		||||
        self.assertIsNotNone(obj.responsible.handler)
 | 
			
		||||
        self.assertEqual(obj.title, data["title"])
 | 
			
		||||
        self.assertEqual(obj.comment, data["comment"])
 | 
			
		||||
        self.assertTrue(test_geom.equals_exact(obj.geometry.geom, 0.000001))
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(obj.legal.binding_date, today)
 | 
			
		||||
        self.assertEqual(obj.legal.registration_date, today)
 | 
			
		||||
        self.assertEqual(obj.responsible.registration_file_number, data["registration_file_number"])
 | 
			
		||||
        self.assertEqual(obj.responsible.conservation_file_number, data["conservation_file_number"])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ShareModalFormTestCase(BaseTestCase):
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
        self.request = RequestFactory().request()
 | 
			
		||||
        self.request.user = self.superuser
 | 
			
		||||
 | 
			
		||||
    def test_init(self):
 | 
			
		||||
        self.intervention.access_token = None
 | 
			
		||||
        self.intervention.save()
 | 
			
		||||
 | 
			
		||||
        form = ShareModalForm(
 | 
			
		||||
            request=self.request,
 | 
			
		||||
            instance=self.intervention
 | 
			
		||||
        )
 | 
			
		||||
        self.assertIsNotNone(self.intervention.access_token)
 | 
			
		||||
        self.assertEqual(form.form_title, str(_("Share")))
 | 
			
		||||
        self.assertEqual(form.form_caption, str(_("Share settings for {}").format(
 | 
			
		||||
            self.intervention.identifier
 | 
			
		||||
        )))
 | 
			
		||||
        self.assertEqual(form.template, "modal/modal_form.html")
 | 
			
		||||
        self.assertEqual(form.instance, self.intervention)
 | 
			
		||||
        self.assertEqual(form.user, self.superuser)
 | 
			
		||||
 | 
			
		||||
    def test_is_valid_and_save(self):
 | 
			
		||||
        # make user default-group-only (special treatment)
 | 
			
		||||
        self.superuser.groups.set(
 | 
			
		||||
            self.groups.filter(
 | 
			
		||||
                name=DEFAULT_GROUP
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.assertNotIn(self.superuser, self.intervention.shared_users)
 | 
			
		||||
        self.assertNotIn(self.team, self.intervention.shared_teams)
 | 
			
		||||
 | 
			
		||||
        # Add new sharing data
 | 
			
		||||
        ## Default-only is able to add new sharing but can not remove existing ones
 | 
			
		||||
        data = {
 | 
			
		||||
            "users": [self.superuser.id,],
 | 
			
		||||
            "teams": [self.team.id,],
 | 
			
		||||
        }
 | 
			
		||||
        form = ShareModalForm(
 | 
			
		||||
            data,
 | 
			
		||||
            request=self.request,
 | 
			
		||||
            instance=self.intervention,
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(form.is_valid(), msg=form.errors)
 | 
			
		||||
        form.save()
 | 
			
		||||
        self.assertIn(self.superuser, self.intervention.shared_users)
 | 
			
		||||
        self.assertIn(self.team, self.intervention.shared_teams)
 | 
			
		||||
 | 
			
		||||
        # Try to remove sharing data das default-only user
 | 
			
		||||
        data = {
 | 
			
		||||
            "users": [],
 | 
			
		||||
            "teams": [],
 | 
			
		||||
        }
 | 
			
		||||
        form = ShareModalForm(
 | 
			
		||||
            data,
 | 
			
		||||
            request=self.request,
 | 
			
		||||
            instance=self.intervention,
 | 
			
		||||
        )
 | 
			
		||||
        self.assertFalse(form.is_valid(), msg=form.errors)
 | 
			
		||||
        self.assertTrue(form.has_error("teams"))
 | 
			
		||||
        self.assertTrue(form.has_error("users"))
 | 
			
		||||
 | 
			
		||||
        # Add another permission group for user
 | 
			
		||||
        self.superuser.groups.add(
 | 
			
		||||
            self.groups.get(
 | 
			
		||||
                name=ZB_GROUP
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        form = ShareModalForm(
 | 
			
		||||
            data,
 | 
			
		||||
            request=self.request,
 | 
			
		||||
            instance=self.intervention,
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(form.is_valid(), msg=form.errors)
 | 
			
		||||
        self.assertIn(self.superuser, self.intervention.shared_users)
 | 
			
		||||
        self.assertIn(self.team, self.intervention.shared_teams)
 | 
			
		||||
 | 
			
		||||
        form.save()
 | 
			
		||||
 | 
			
		||||
        self.assertNotIn(self.superuser, self.intervention.shared_users)
 | 
			
		||||
        self.assertNotIn(self.team, self.intervention.shared_teams)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NewRevocationModalFormTestCase(BaseTestCase):
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
        self.request = RequestFactory().request()
 | 
			
		||||
        self.request.user = self.superuser
 | 
			
		||||
 | 
			
		||||
    def test_init(self):
 | 
			
		||||
        form = NewRevocationModalForm(
 | 
			
		||||
            request=self.request,
 | 
			
		||||
            instance=self.intervention
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(form.instance, self.intervention)
 | 
			
		||||
        self.assertEqual(form.user, self.request.user)
 | 
			
		||||
        self.assertEqual(form.request, self.request)
 | 
			
		||||
        self.assertEqual(form.form_title, str(_("Add revocation")))
 | 
			
		||||
        self.assertEqual(form.form_caption, "")
 | 
			
		||||
        self.assertEqual(form.form_attrs, {
 | 
			
		||||
            "enctype": "multipart/form-data"
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
    def test_save(self):
 | 
			
		||||
        data = {
 | 
			
		||||
            "date": now().date(),
 | 
			
		||||
            "file": None,
 | 
			
		||||
            "comment": generate_random_string(20, use_letters_uc=True)
 | 
			
		||||
        }
 | 
			
		||||
        form = NewRevocationModalForm(
 | 
			
		||||
            data,
 | 
			
		||||
            request=self.request,
 | 
			
		||||
            instance=self.intervention
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(form.is_valid(), msg=form.errors)
 | 
			
		||||
        obj = form.save()
 | 
			
		||||
        self.assertEqual(obj.intervention, self.intervention)
 | 
			
		||||
        self.assertEqual(obj.date, data["date"])
 | 
			
		||||
        self.assertEqual(obj.legal, self.intervention.legal)
 | 
			
		||||
        self.assertEqual(obj.comment, data["comment"])
 | 
			
		||||
 | 
			
		||||
        last_log = self.intervention.log.first()
 | 
			
		||||
        self.assertEqual(last_log.user, self.superuser)
 | 
			
		||||
        self.assertEqual(last_log.action, UserAction.EDITED)
 | 
			
		||||
        self.assertEqual(last_log.comment, REVOCATION_ADDED)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EditRevocationModalFormTestCase(NewRevocationModalFormTestCase):
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
        self.revoc = Revocation.objects.get_or_create(
 | 
			
		||||
            date=now().date(),
 | 
			
		||||
            comment="TEST",
 | 
			
		||||
            legal=self.intervention.legal,
 | 
			
		||||
        )[0]
 | 
			
		||||
 | 
			
		||||
    def test_init(self):
 | 
			
		||||
        new_date = now().date() - timedelta(days=10)
 | 
			
		||||
        data = {
 | 
			
		||||
            "date": new_date,
 | 
			
		||||
            "comment": generate_random_string(20, use_letters_lc=True)
 | 
			
		||||
        }
 | 
			
		||||
        form = EditRevocationModalForm(
 | 
			
		||||
            data,
 | 
			
		||||
            request=self.request,
 | 
			
		||||
            instance=self.intervention,
 | 
			
		||||
            revocation=self.revoc
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(form.is_valid(), msg=form.errors)
 | 
			
		||||
        obj = form.save()
 | 
			
		||||
        self.assertEqual(obj.date, new_date)
 | 
			
		||||
        self.assertEqual(obj.comment, data["comment"])
 | 
			
		||||
        self.assertEqual(obj.legal, self.intervention.legal)
 | 
			
		||||
        self.assertEqual(obj.intervention, self.intervention)
 | 
			
		||||
 | 
			
		||||
        last_log = self.intervention.log.first()
 | 
			
		||||
        self.assertEqual(last_log.action, UserAction.EDITED)
 | 
			
		||||
        self.assertEqual(last_log.user, self.superuser)
 | 
			
		||||
        self.assertEqual(last_log.comment, REVOCATION_EDITED)
 | 
			
		||||
 | 
			
		||||
class RemoveRevocationModalFormTestCase(EditRevocationModalFormTestCase):
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
 | 
			
		||||
    def test_init(self):
 | 
			
		||||
        form = RemoveRevocationModalForm(
 | 
			
		||||
            request=self.request,
 | 
			
		||||
            instance=self.intervention,
 | 
			
		||||
            revocation=self.revoc,
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(form.instance, self.intervention)
 | 
			
		||||
        self.assertEqual(form.revocation, self.revoc)
 | 
			
		||||
        self.assertEqual(form.request, self.request)
 | 
			
		||||
        self.assertEqual(form.user, self.request.user)
 | 
			
		||||
 | 
			
		||||
    def test_save(self):
 | 
			
		||||
        data = {
 | 
			
		||||
            "confirm": True,
 | 
			
		||||
        }
 | 
			
		||||
        form = RemoveRevocationModalForm(
 | 
			
		||||
            data,
 | 
			
		||||
            request=self.request,
 | 
			
		||||
            instance=self.intervention,
 | 
			
		||||
            revocation=self.revoc
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(form.is_valid(), msg=form.errors)
 | 
			
		||||
        form.save()
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            self.revoc.refresh_from_db()
 | 
			
		||||
            self.fail("Revocation should not exist anymore")
 | 
			
		||||
        except ObjectDoesNotExist:
 | 
			
		||||
            pass
 | 
			
		||||
        last_log = self.intervention.log.first()
 | 
			
		||||
        self.assertEqual(last_log.action, UserAction.EDITED)
 | 
			
		||||
        self.assertEqual(last_log.user, self.superuser)
 | 
			
		||||
        self.assertEqual(last_log.comment, REVOCATION_REMOVED)
 | 
			
		||||
							
								
								
									
										50
									
								
								intervention/tests/unit/test_models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								intervention/tests/unit/test_models.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,50 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: ksp-servicestelle@sgdnord.rlp.de
 | 
			
		||||
Created on: 07.09.23
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
from django.core.exceptions import ObjectDoesNotExist
 | 
			
		||||
from django.utils.timezone import now
 | 
			
		||||
 | 
			
		||||
from intervention.models import RevocationDocument, Revocation
 | 
			
		||||
from konova.tests.test_views import BaseTestCase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RevocationDocumentTestCase(BaseTestCase):
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
        self.revocation = Revocation.objects.get_or_create(
 | 
			
		||||
            date=now().date(),
 | 
			
		||||
            comment="Test",
 | 
			
		||||
            legal=self.intervention.legal
 | 
			
		||||
        )[0]
 | 
			
		||||
        self.doc = self.create_dummy_document(
 | 
			
		||||
            RevocationDocument,
 | 
			
		||||
            instance=self.revocation
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_intervention_property(self):
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            self.doc.intervention,
 | 
			
		||||
            self.doc.instance.legal.intervention
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            self.doc.intervention,
 | 
			
		||||
            self.intervention
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_delete(self):
 | 
			
		||||
        revoc_docs, other_intervention_docs = self.intervention.get_documents()
 | 
			
		||||
        self.assertIn(self.doc, revoc_docs)
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            self.doc.delete()
 | 
			
		||||
            self.doc.refresh_from_db()
 | 
			
		||||
            self.fail("Should not be fetchable anymore!")
 | 
			
		||||
        except ObjectDoesNotExist:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        revoc_docs, other_intervention_docs = self.intervention.get_documents()
 | 
			
		||||
        self.assertEqual(revoc_docs.count(), 0)
 | 
			
		||||
@ -22,8 +22,7 @@ from konova.forms.modals import RemoveModalForm
 | 
			
		||||
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
 | 
			
		||||
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
 | 
			
		||||
from konova.utils.message_templates import DATA_CHECKED_PREVIOUSLY_TEMPLATE, RECORDED_BLOCKS_EDIT, \
 | 
			
		||||
    CHECKED_RECORDED_RESET, FORM_INVALID, IDENTIFIER_REPLACED, DO_NOT_FORGET_TO_SHARE, GEOMETRY_SIMPLIFIED
 | 
			
		||||
from konova.utils.user_checks import in_group
 | 
			
		||||
    CHECK_STATE_RESET, FORM_INVALID, IDENTIFIER_REPLACED, DO_NOT_FORGET_TO_SHARE, GEOMETRY_SIMPLIFIED
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@login_required
 | 
			
		||||
@ -186,9 +185,9 @@ def detail_view(request: HttpRequest, id: str):
 | 
			
		||||
        "compensations": compensations,
 | 
			
		||||
        "has_access": is_data_shared,
 | 
			
		||||
        "geom_form": geom_form,
 | 
			
		||||
        "is_default_member": in_group(_user, DEFAULT_GROUP),
 | 
			
		||||
        "is_zb_member": in_group(_user, ZB_GROUP),
 | 
			
		||||
        "is_ets_member": in_group(_user, ETS_GROUP),
 | 
			
		||||
        "is_default_member": _user.in_group(DEFAULT_GROUP),
 | 
			
		||||
        "is_zb_member": _user.in_group(ZB_GROUP),
 | 
			
		||||
        "is_ets_member": _user.in_group(ETS_GROUP),
 | 
			
		||||
        "LANIS_LINK": intervention.get_LANIS_link(),
 | 
			
		||||
        "has_payment_without_document": has_payment_without_document,
 | 
			
		||||
        TAB_TITLE_IDENTIFIER: f"{intervention.identifier} - {intervention.title}",
 | 
			
		||||
@ -230,12 +229,11 @@ def edit_view(request: HttpRequest, id: str):
 | 
			
		||||
        if data_form.is_valid() and geom_form.is_valid():
 | 
			
		||||
            # The data form takes the geom form for processing, as well as the performing user
 | 
			
		||||
            # Save the current state of recorded|checked to inform the user in case of a status reset due to editing
 | 
			
		||||
            i_rec = intervention.recorded is not None
 | 
			
		||||
            i_check = intervention.checked is not None
 | 
			
		||||
            intervention_is_checked = intervention.checked is not None
 | 
			
		||||
            intervention = data_form.save(request.user, geom_form)
 | 
			
		||||
            messages.success(request, _("Intervention {} edited").format(intervention.identifier))
 | 
			
		||||
            if i_check or i_rec:
 | 
			
		||||
                messages.info(request, CHECKED_RECORDED_RESET)
 | 
			
		||||
            if intervention_is_checked:
 | 
			
		||||
                messages.info(request, CHECK_STATE_RESET)
 | 
			
		||||
            if geom_form.geometry_simplified:
 | 
			
		||||
                messages.info(
 | 
			
		||||
                    request,
 | 
			
		||||
 | 
			
		||||
@ -8,10 +8,10 @@ Created on: 15.08.22
 | 
			
		||||
import json
 | 
			
		||||
 | 
			
		||||
from django.contrib.gis import gdal
 | 
			
		||||
from django.contrib.gis.forms import MultiPolygonField
 | 
			
		||||
from django.contrib.gis.geos import MultiPolygon, Polygon
 | 
			
		||||
from django.contrib.gis.geos.prototypes.io import WKTWriter
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
from django.forms import JSONField
 | 
			
		||||
 | 
			
		||||
from konova.forms.base_form import BaseForm
 | 
			
		||||
from konova.models import Geometry
 | 
			
		||||
@ -27,8 +27,7 @@ class SimpleGeomForm(BaseForm):
 | 
			
		||||
    """
 | 
			
		||||
    read_only = True
 | 
			
		||||
    geometry_simplified = False
 | 
			
		||||
    geom = MultiPolygonField(
 | 
			
		||||
        srid=DEFAULT_SRID_RLP,
 | 
			
		||||
    geom = JSONField(
 | 
			
		||||
        label=_("Geometry"),
 | 
			
		||||
        help_text=_(""),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,12 @@ from user.models import UserActionLogEntry, User
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RemoveForm(BaseForm):
 | 
			
		||||
    """ DEPRECATED
 | 
			
		||||
 | 
			
		||||
    NOT USED IN ANY PLACE.
 | 
			
		||||
    CAN BE DELETED AT SOME POINT.
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    check = forms.BooleanField(
 | 
			
		||||
        label=_("Confirm"),
 | 
			
		||||
        label_suffix=_(""),
 | 
			
		||||
 | 
			
		||||
@ -317,7 +317,7 @@ class Geometry(BaseResource):
 | 
			
		||||
        """
 | 
			
		||||
        geom = self.geom
 | 
			
		||||
        if geom.srid != srid:
 | 
			
		||||
            geom.transform(ct=srid)
 | 
			
		||||
            geom.transform(srid)
 | 
			
		||||
        polygons = [p for p in geom]
 | 
			
		||||
        geojson = {
 | 
			
		||||
            "type": "FeatureCollection",
 | 
			
		||||
 | 
			
		||||
@ -23,13 +23,9 @@ from django.core.exceptions import ObjectDoesNotExist
 | 
			
		||||
from django.http import HttpRequest
 | 
			
		||||
from django.utils.timezone import now
 | 
			
		||||
from django.db import models, transaction
 | 
			
		||||
from compensation.settings import COMPENSATION_IDENTIFIER_TEMPLATE, COMPENSATION_IDENTIFIER_LENGTH, \
 | 
			
		||||
    ECO_ACCOUNT_IDENTIFIER_TEMPLATE, ECO_ACCOUNT_IDENTIFIER_LENGTH
 | 
			
		||||
from ema.settings import EMA_ACCOUNT_IDENTIFIER_LENGTH, EMA_ACCOUNT_IDENTIFIER_TEMPLATE
 | 
			
		||||
from intervention.settings import INTERVENTION_IDENTIFIER_LENGTH, INTERVENTION_IDENTIFIER_TEMPLATE
 | 
			
		||||
from konova.utils import generators
 | 
			
		||||
from konova.utils.generators import generate_random_string
 | 
			
		||||
from konova.utils.message_templates import CHECKED_RECORDED_RESET, GEOMETRY_CONFLICT_WITH_TEMPLATE
 | 
			
		||||
from konova.utils.message_templates import GEOMETRY_CONFLICT_WITH_TEMPLATE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UuidModel(models.Model):
 | 
			
		||||
@ -143,6 +139,9 @@ class BaseObject(BaseResource, DeletableObjectMixin):
 | 
			
		||||
    comment = models.TextField(null=True, blank=True)
 | 
			
		||||
    log = models.ManyToManyField("user.UserActionLogEntry", blank=True, help_text="Keeps all user actions of an object", editable=False)
 | 
			
		||||
 | 
			
		||||
    identifier_length = 6                   # Fallback - specified in inheriting classes
 | 
			
		||||
    identifier_template = "UNBEKANNT-{}"    # Fallback - specified in inheriting classes
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        abstract = True
 | 
			
		||||
 | 
			
		||||
@ -193,32 +192,8 @@ class BaseObject(BaseResource, DeletableObjectMixin):
 | 
			
		||||
        Returns:
 | 
			
		||||
            str
 | 
			
		||||
        """
 | 
			
		||||
        from compensation.models import Compensation, EcoAccount
 | 
			
		||||
        from intervention.models import Intervention
 | 
			
		||||
        from ema.models import Ema
 | 
			
		||||
 | 
			
		||||
        definitions = {
 | 
			
		||||
            Intervention: {
 | 
			
		||||
                "length": INTERVENTION_IDENTIFIER_LENGTH,
 | 
			
		||||
                "template": INTERVENTION_IDENTIFIER_TEMPLATE,
 | 
			
		||||
            },
 | 
			
		||||
            Compensation: {
 | 
			
		||||
                "length": COMPENSATION_IDENTIFIER_LENGTH,
 | 
			
		||||
                "template": COMPENSATION_IDENTIFIER_TEMPLATE,
 | 
			
		||||
            },
 | 
			
		||||
            EcoAccount: {
 | 
			
		||||
                "length": ECO_ACCOUNT_IDENTIFIER_LENGTH,
 | 
			
		||||
                "template": ECO_ACCOUNT_IDENTIFIER_TEMPLATE,
 | 
			
		||||
            },
 | 
			
		||||
            Ema: {
 | 
			
		||||
                "length": EMA_ACCOUNT_IDENTIFIER_LENGTH,
 | 
			
		||||
                "template": EMA_ACCOUNT_IDENTIFIER_TEMPLATE,
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if self.__class__ not in definitions:
 | 
			
		||||
            # Not defined, yet. Create fallback identifier for this case
 | 
			
		||||
            return generate_random_string(10)
 | 
			
		||||
        id_len = self.identifier_length
 | 
			
		||||
        id_template = self.identifier_template
 | 
			
		||||
 | 
			
		||||
        _now = now()
 | 
			
		||||
        curr_month = _now.month
 | 
			
		||||
@ -229,13 +204,13 @@ class BaseObject(BaseResource, DeletableObjectMixin):
 | 
			
		||||
            curr_month = str(curr_month)
 | 
			
		||||
        curr_year = str(_now.year)
 | 
			
		||||
        rand_str = generate_random_string(
 | 
			
		||||
            length=definitions[self.__class__]["length"],
 | 
			
		||||
            length=id_len,
 | 
			
		||||
            use_numbers=True,
 | 
			
		||||
            use_letters_lc=False,
 | 
			
		||||
            use_letters_uc=True,
 | 
			
		||||
        )
 | 
			
		||||
        _str = "{}{}-{}".format(curr_month, curr_year, rand_str)
 | 
			
		||||
        return definitions[self.__class__]["template"].format(_str)
 | 
			
		||||
        return id_template.format(_str)
 | 
			
		||||
 | 
			
		||||
    @abstractmethod
 | 
			
		||||
    def get_detail_url(self):
 | 
			
		||||
@ -319,27 +294,6 @@ class RecordableObjectMixin(models.Model):
 | 
			
		||||
 | 
			
		||||
        return action
 | 
			
		||||
 | 
			
		||||
    def unrecord(self, performing_user, request: HttpRequest = None):
 | 
			
		||||
        """ Unrecords a dataset
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            performing_user (User): The user which performed the editing action
 | 
			
		||||
            request (HttpRequest): The used request for this action
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        action = None
 | 
			
		||||
        if self.recorded:
 | 
			
		||||
            action = self.set_unrecorded(performing_user)
 | 
			
		||||
            self.log.add(action)
 | 
			
		||||
            if request:
 | 
			
		||||
                messages.info(
 | 
			
		||||
                    request,
 | 
			
		||||
                    CHECKED_RECORDED_RESET
 | 
			
		||||
                )
 | 
			
		||||
        return action
 | 
			
		||||
 | 
			
		||||
    @abstractmethod
 | 
			
		||||
    def is_ready_for_publish(self) -> bool:
 | 
			
		||||
        """ Check for all needed publishing-constraints on the data
 | 
			
		||||
@ -374,7 +328,7 @@ class CheckableObjectMixin(models.Model):
 | 
			
		||||
        abstract = True
 | 
			
		||||
 | 
			
		||||
    def set_unchecked(self) -> None:
 | 
			
		||||
        """ Perform unrecording
 | 
			
		||||
        """ Perform unchecking
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
 | 
			
		||||
@ -384,7 +338,7 @@ class CheckableObjectMixin(models.Model):
 | 
			
		||||
        if not self.checked:
 | 
			
		||||
            # Nothing to do
 | 
			
		||||
            return
 | 
			
		||||
        # Do not .delete() the checked attribute! Just set it to None, since a delete() would kill it out of the
 | 
			
		||||
        # Do not .delete() the checked attribute! Just set it to None, since a delete() would remove it from the
 | 
			
		||||
        # log history, which is not what we want!
 | 
			
		||||
        self.checked = None
 | 
			
		||||
        self.save()
 | 
			
		||||
@ -686,12 +640,11 @@ class ShareableObjectMixin(models.Model):
 | 
			
		||||
        Returns:
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        from konova.utils.user_checks import is_default_group_only
 | 
			
		||||
        users = self.shared_users
 | 
			
		||||
        cleaned_users = []
 | 
			
		||||
        default_users = []
 | 
			
		||||
        for user in users:
 | 
			
		||||
            if not is_default_group_only(user):
 | 
			
		||||
            if not user.is_default_group_only():
 | 
			
		||||
                cleaned_users.append(user)
 | 
			
		||||
            else:
 | 
			
		||||
                default_users.append(user)
 | 
			
		||||
 | 
			
		||||
@ -15,7 +15,7 @@ DEFAULT_SRID_RLP = 25832
 | 
			
		||||
 | 
			
		||||
# Needed to redirect to LANIS
 | 
			
		||||
## Values to be inserted are [zoom_level, x_coord, y_coord]
 | 
			
		||||
LANIS_LINK_TEMPLATE = "https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/index.php?lang=de&zl={}&x={}&y={}&bl=tk_rlp_tms_grau&bo=1&lo=0.8,0.8,0.8,0.6,0.8,0.8,0.8,0.8,0.8&layers=eiv_recorded,eiv_unrecorded,kom_recorded,kom_unrecorded,oek_recorded,oek_unrecorded,ema_recorded,ema_unrecorded,mae&service=kartendienste_naturschutz"
 | 
			
		||||
LANIS_LINK_TEMPLATE = "https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/index.php?lang=de&zl={}&x={}&y={}&bl=tk_rlp_tms_grau&bo=1&lo=0.8,0.8,0.8,0.6,0.8,0.8,0.8,0.8,0.8&layers=eiv_recorded,eiv_unrecorded,eiv_unrecorded_old_entries,kom_recorded,kom_unrecorded,kom_unrecorded_old_entries,oek_recorded,oek_unrecorded,ema_recorded,ema_unrecorded,mae&service=kartendienste_naturschutz"
 | 
			
		||||
## This look up table (LUT) defines different zoom levels on the size of the calculate area of a geometry.
 | 
			
		||||
LANIS_ZOOM_LUT = {
 | 
			
		||||
    1000000000: 6,
 | 
			
		||||
 | 
			
		||||
@ -5,9 +5,12 @@ Contact: michel.peltriaux@sgdnord.rlp.de
 | 
			
		||||
Created on: 15.12.21
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
import json
 | 
			
		||||
 | 
			
		||||
from django.contrib.gis.db.models.functions import Translate
 | 
			
		||||
 | 
			
		||||
from konova.models import Geometry, GeometryConflict
 | 
			
		||||
from konova.sub_settings.lanis_settings import DEFAULT_SRID
 | 
			
		||||
from konova.tests.test_views import BaseTestCase
 | 
			
		||||
from konova.utils.schneider.fetcher import ParcelFetcher
 | 
			
		||||
 | 
			
		||||
@ -74,3 +77,66 @@ class GeometryTestCase(BaseTestCase):
 | 
			
		||||
        fetcher = ParcelFetcher(geometry=self.geom_1)
 | 
			
		||||
        features = fetcher.get_parcels()
 | 
			
		||||
        self.assertNotEqual(0, len(features), msg="Spatial fetcher get feature did not work!")
 | 
			
		||||
 | 
			
		||||
    def test_str(self):
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            str(self.geom_1),
 | 
			
		||||
            str(self.geom_1.id)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_get_data_objects(self):
 | 
			
		||||
        num_objs_with_geom = 0
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            len(self.geom_1.get_data_objects()),
 | 
			
		||||
            num_objs_with_geom
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        objs = [
 | 
			
		||||
            self.intervention,
 | 
			
		||||
            self.compensation,
 | 
			
		||||
            self.eco_account,
 | 
			
		||||
            self.ema,
 | 
			
		||||
        ]
 | 
			
		||||
        for obj in objs:
 | 
			
		||||
            obj.geometry = self.geom_1
 | 
			
		||||
            obj.save()
 | 
			
		||||
 | 
			
		||||
            num_objs_with_geom += 1
 | 
			
		||||
            geom_objs = self.geom_1.get_data_objects()
 | 
			
		||||
            self.assertEqual(
 | 
			
		||||
                len(geom_objs),
 | 
			
		||||
                num_objs_with_geom
 | 
			
		||||
            )
 | 
			
		||||
            self.assertIn(obj, geom_objs)
 | 
			
		||||
 | 
			
		||||
    def test_as_feature_collection(self):
 | 
			
		||||
        geometry = self.geom_1.geom
 | 
			
		||||
        polygons = [p for p in geometry]
 | 
			
		||||
        expected_result = {
 | 
			
		||||
            "type": "FeatureCollection",
 | 
			
		||||
            "crs": {
 | 
			
		||||
                "type": "name",
 | 
			
		||||
                "properties": {
 | 
			
		||||
                    "name": f"urn:ogc:def:crs:EPSG::{geometry.srid}"
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            "features": [
 | 
			
		||||
                {
 | 
			
		||||
                    "type": "Feature",
 | 
			
		||||
                    "geometry": json.loads(p.json),
 | 
			
		||||
                }
 | 
			
		||||
                for p in polygons
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
        result = self.geom_1.as_feature_collection()
 | 
			
		||||
        result = json.dumps(result)
 | 
			
		||||
        expected_result = json.dumps(expected_result)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(expected_result, result)
 | 
			
		||||
 | 
			
		||||
        # Transform geometry into non-default-rlp srid to trigger retransforming in later steps
 | 
			
		||||
        geometry.transform(DEFAULT_SRID)
 | 
			
		||||
        different_result = self.geom_1.as_feature_collection()
 | 
			
		||||
        different_result = json.dumps(result)
 | 
			
		||||
        self.assertNotEqual(different_result, result)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,7 @@ Created on: 26.10.21
 | 
			
		||||
import datetime
 | 
			
		||||
import json
 | 
			
		||||
 | 
			
		||||
from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID
 | 
			
		||||
from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID, CODELIST_REGISTRATION_OFFICE_ID
 | 
			
		||||
from ema.models import Ema
 | 
			
		||||
from konova.sub_settings.lanis_settings import DEFAULT_SRID_RLP
 | 
			
		||||
from user.models import User, Team
 | 
			
		||||
@ -157,14 +157,17 @@ class BaseTestCase(TestCase):
 | 
			
		||||
        intervention.generate_access_token(make_unique=True)
 | 
			
		||||
        return intervention
 | 
			
		||||
 | 
			
		||||
    def create_dummy_compensation(self):
 | 
			
		||||
    def create_dummy_compensation(self, interv: Intervention=None):
 | 
			
		||||
        """ Creates a compensation which can be used for tests
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        if self.intervention is None:
 | 
			
		||||
            self.intervention = self.create_dummy_intervention()
 | 
			
		||||
        if not interv:
 | 
			
		||||
            if self.intervention is None:
 | 
			
		||||
                interv = self.create_dummy_intervention()
 | 
			
		||||
            else:
 | 
			
		||||
                interv = self.intervention
 | 
			
		||||
        # Create dummy data
 | 
			
		||||
        # Create log entry
 | 
			
		||||
        action = UserActionLogEntry.get_created_action(self.superuser)
 | 
			
		||||
@ -173,7 +176,7 @@ class BaseTestCase(TestCase):
 | 
			
		||||
        compensation = Compensation.objects.create(
 | 
			
		||||
            identifier="TEST",
 | 
			
		||||
            title="Test_title",
 | 
			
		||||
            intervention=self.intervention,
 | 
			
		||||
            intervention=interv,
 | 
			
		||||
            created=action,
 | 
			
		||||
            geometry=geometry,
 | 
			
		||||
            comment="Test",
 | 
			
		||||
@ -196,9 +199,11 @@ class BaseTestCase(TestCase):
 | 
			
		||||
        handler = self.handler
 | 
			
		||||
        responsible_data.handler = handler
 | 
			
		||||
        responsible_data.save()
 | 
			
		||||
 | 
			
		||||
        identifier = EcoAccount().generate_new_identifier()
 | 
			
		||||
        # Finally create main object, holding the other objects
 | 
			
		||||
        eco_account = EcoAccount.objects.create(
 | 
			
		||||
            identifier="TEST",
 | 
			
		||||
            identifier=identifier,
 | 
			
		||||
            title="Test_title",
 | 
			
		||||
            deductable_surface=500,
 | 
			
		||||
            legal=lega_data,
 | 
			
		||||
@ -234,10 +239,15 @@ class BaseTestCase(TestCase):
 | 
			
		||||
        )
 | 
			
		||||
        return ema
 | 
			
		||||
 | 
			
		||||
    def create_dummy_deduction(self):
 | 
			
		||||
    def create_dummy_deduction(self, acc: EcoAccount = None, interv: Intervention = None):
 | 
			
		||||
        if not acc:
 | 
			
		||||
            acc = self.create_dummy_eco_account()
 | 
			
		||||
        if not interv:
 | 
			
		||||
            interv = self.create_dummy_intervention()
 | 
			
		||||
 | 
			
		||||
        return EcoAccountDeduction.objects.create(
 | 
			
		||||
            account=self.create_dummy_eco_account(),
 | 
			
		||||
            intervention=self.create_dummy_intervention(),
 | 
			
		||||
            account=acc,
 | 
			
		||||
            intervention=interv,
 | 
			
		||||
            surface=100,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
@ -270,15 +280,17 @@ class BaseTestCase(TestCase):
 | 
			
		||||
        Returns:
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        codes = KonovaCode.objects.bulk_create([
 | 
			
		||||
            KonovaCode(id=1, is_selectable=True, is_archived=False, is_leaf=True, long_name="Test1"),
 | 
			
		||||
            KonovaCode(id=2, is_selectable=True, is_archived=False, is_leaf=True, long_name="Test2"),
 | 
			
		||||
            KonovaCode(id=3, is_selectable=True, is_archived=False, is_leaf=True, long_name="Test3"),
 | 
			
		||||
            KonovaCode(id=4, is_selectable=True, is_archived=False, is_leaf=True, long_name="Test4"),
 | 
			
		||||
        ])
 | 
			
		||||
        codes = KonovaCode.objects.all()
 | 
			
		||||
        if codes.count() == 0:
 | 
			
		||||
            codes = KonovaCode.objects.bulk_create([
 | 
			
		||||
                KonovaCode(id=1, is_selectable=True, is_archived=False, is_leaf=True, long_name="Test1"),
 | 
			
		||||
                KonovaCode(id=2, is_selectable=True, is_archived=False, is_leaf=True, long_name="Test2"),
 | 
			
		||||
                KonovaCode(id=3, is_selectable=True, is_archived=False, is_leaf=True, long_name="Test3"),
 | 
			
		||||
                KonovaCode(id=4, is_selectable=True, is_archived=False, is_leaf=True, long_name="Test4"),
 | 
			
		||||
            ])
 | 
			
		||||
        return codes
 | 
			
		||||
 | 
			
		||||
    def create_dummy_team(self):
 | 
			
		||||
    def create_dummy_team(self, name: str = None):
 | 
			
		||||
        """ Creates a dummy team
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
@ -287,8 +299,11 @@ class BaseTestCase(TestCase):
 | 
			
		||||
        if self.superuser is None:
 | 
			
		||||
            self.create_users()
 | 
			
		||||
 | 
			
		||||
        if not name:
 | 
			
		||||
            name = "Testteam"
 | 
			
		||||
 | 
			
		||||
        team = Team.objects.get_or_create(
 | 
			
		||||
            name="Testteam",
 | 
			
		||||
            name=name,
 | 
			
		||||
            description="Testdescription",
 | 
			
		||||
        )[0]
 | 
			
		||||
        team.users.add(self.superuser)
 | 
			
		||||
@ -318,7 +333,7 @@ class BaseTestCase(TestCase):
 | 
			
		||||
        """
 | 
			
		||||
        polygon = Polygon.from_bbox((7.592449, 50.359385, 7.593382, 50.359874))
 | 
			
		||||
        polygon.srid = 4326
 | 
			
		||||
        polygon = polygon.transform(DEFAULT_SRID_RLP, clone=True)
 | 
			
		||||
        polygon.transform(DEFAULT_SRID_RLP)
 | 
			
		||||
        return MultiPolygon(polygon, srid=DEFAULT_SRID_RLP)
 | 
			
		||||
 | 
			
		||||
    def create_geojson(self, geometry):
 | 
			
		||||
@ -408,6 +423,19 @@ class BaseTestCase(TestCase):
 | 
			
		||||
        codelist.codes.add(code)
 | 
			
		||||
        return code
 | 
			
		||||
 | 
			
		||||
    def get_registration_office_code(self):
 | 
			
		||||
        """ Returns a dummy KonovaCode as conservation office code
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        codelist = KonovaCodeList.objects.get_or_create(
 | 
			
		||||
            id=CODELIST_REGISTRATION_OFFICE_ID
 | 
			
		||||
        )[0]
 | 
			
		||||
        code = KonovaCode.objects.get(id=3)
 | 
			
		||||
        codelist.codes.add(code)
 | 
			
		||||
        return code
 | 
			
		||||
 | 
			
		||||
    def fill_out_ema(self, ema):
 | 
			
		||||
        """ Adds all required (dummy) data to an Ema
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										365
									
								
								konova/tests/unit/test_forms.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										365
									
								
								konova/tests/unit/test_forms.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,365 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: ksp-servicestelle@sgdnord.rlp.de
 | 
			
		||||
Created on: 07.09.23
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
import mimetypes
 | 
			
		||||
from datetime import timedelta
 | 
			
		||||
 | 
			
		||||
from django.core.exceptions import ObjectDoesNotExist
 | 
			
		||||
from django.test import RequestFactory
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
from django.core.files.uploadedfile import SimpleUploadedFile
 | 
			
		||||
from django.utils.timezone import now
 | 
			
		||||
 | 
			
		||||
from compensation.forms.modals.document import NewEcoAccountDocumentModalForm, NewCompensationDocumentModalForm
 | 
			
		||||
from compensation.models import Payment
 | 
			
		||||
from ema.forms import NewEmaDocumentModalForm
 | 
			
		||||
from intervention.forms.modals.document import NewInterventionDocumentModalForm
 | 
			
		||||
from intervention.models import InterventionDocument
 | 
			
		||||
from konova.forms.modals import EditDocumentModalForm, NewDocumentModalForm, RecordModalForm, RemoveModalForm, \
 | 
			
		||||
    RemoveDeadlineModalForm, ResubmissionModalForm
 | 
			
		||||
from konova.models import Resubmission
 | 
			
		||||
from konova.tests.test_views import BaseTestCase
 | 
			
		||||
from konova.utils.generators import generate_random_string
 | 
			
		||||
from konova.utils.message_templates import DOCUMENT_EDITED, DEADLINE_REMOVED
 | 
			
		||||
from user.models import UserAction
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NewDocumentModalFormTestCase(BaseTestCase):
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
        self.request = RequestFactory().request()
 | 
			
		||||
        self.request.user = self.superuser
 | 
			
		||||
        self.dummy_file = SimpleUploadedFile(
 | 
			
		||||
            "some_file.pdf",
 | 
			
		||||
            b"Some conent in this file",
 | 
			
		||||
            mimetypes.types_map[".pdf"]
 | 
			
		||||
        )
 | 
			
		||||
        dummy_file_dict = {
 | 
			
		||||
            "file": self.dummy_file
 | 
			
		||||
        }
 | 
			
		||||
        self.data = {
 | 
			
		||||
            "title": generate_random_string(length=5, use_letters_lc=True),
 | 
			
		||||
            "creation_date": now().date(),
 | 
			
		||||
            "comment": generate_random_string(length=50, use_letters_uc=True),
 | 
			
		||||
        }
 | 
			
		||||
        self.forms = [
 | 
			
		||||
            NewInterventionDocumentModalForm(self.data, dummy_file_dict, request=self.request, instance=self.intervention),
 | 
			
		||||
            NewCompensationDocumentModalForm(self.data, dummy_file_dict, request=self.request, instance=self.compensation),
 | 
			
		||||
            NewEmaDocumentModalForm(self.data, dummy_file_dict, request=self.request, instance=self.ema),
 | 
			
		||||
            NewEcoAccountDocumentModalForm(self.data, dummy_file_dict, request=self.request, instance=self.eco_account),
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
    def test_init(self):
 | 
			
		||||
        for form in self.forms:
 | 
			
		||||
            self.assertEqual(form.form_title, str(_("Add new document")))
 | 
			
		||||
            self.assertEqual(form.form_caption, str(_("")))
 | 
			
		||||
            self.assertEqual(
 | 
			
		||||
                form.form_attrs,
 | 
			
		||||
                {
 | 
			
		||||
                    "enctype": "multipart/form-data"
 | 
			
		||||
                }
 | 
			
		||||
            )
 | 
			
		||||
            self.assertEqual(form.request, self.request)
 | 
			
		||||
            self.assertEqual(form.user, self.superuser)
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            NewDocumentModalForm(request=self.request, instance=self.intervention)
 | 
			
		||||
            self.fail("Base form NewDocumentModalForm should not be creatable")
 | 
			
		||||
        except NotImplementedError:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
    def test_is_valid(self):
 | 
			
		||||
        for form in self.forms:
 | 
			
		||||
            self.assertTrue(
 | 
			
		||||
                form.is_valid(), msg=form.errors
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    def test_save(self):
 | 
			
		||||
        for form in self.forms:
 | 
			
		||||
            form.is_valid()
 | 
			
		||||
            obj = form.save()
 | 
			
		||||
            self.assertEqual(obj.created.action, UserAction.CREATED)
 | 
			
		||||
            self.assertEqual(obj.created.user, self.superuser)
 | 
			
		||||
            self.assertEqual(obj.title, self.data["title"])
 | 
			
		||||
            self.assertEqual(obj.date_of_creation, self.data["creation_date"])
 | 
			
		||||
            self.assertEqual(obj.comment, self.data["comment"])
 | 
			
		||||
            self.assertIsNotNone(obj.file)
 | 
			
		||||
 | 
			
		||||
            last_log = obj.instance.log.first()
 | 
			
		||||
            self.assertEqual(last_log.action, UserAction.EDITED)
 | 
			
		||||
            self.assertEqual(last_log.user, self.request.user)
 | 
			
		||||
            self.assertEqual(last_log.comment, str(_("Added document")))
 | 
			
		||||
            self.assertEqual(obj.instance.modified, last_log)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EditDocumentModalFormTestCase(NewDocumentModalFormTestCase):
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
        dummy_file_dict = {
 | 
			
		||||
            "file": self.dummy_file
 | 
			
		||||
        }
 | 
			
		||||
        self.doc = self.create_dummy_document(
 | 
			
		||||
            InterventionDocument,
 | 
			
		||||
            instance=self.intervention
 | 
			
		||||
        )
 | 
			
		||||
        self.form = EditDocumentModalForm(
 | 
			
		||||
            self.data,
 | 
			
		||||
            dummy_file_dict,
 | 
			
		||||
            request=self.request,
 | 
			
		||||
            instance=self.intervention,
 | 
			
		||||
            document=self.doc
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_init(self):
 | 
			
		||||
        self.assertEqual(self.form.form_title, str(_("Edit document")))
 | 
			
		||||
        self.assertEqual(self.form.document, self.doc)
 | 
			
		||||
        self.assertEqual(self.form.request, self.request)
 | 
			
		||||
        self.assertEqual(self.form.user, self.request.user)
 | 
			
		||||
        self.assertEqual(self.form.fields["title"].initial, self.doc.title)
 | 
			
		||||
        self.assertEqual(self.form.fields["comment"].initial, self.doc.comment)
 | 
			
		||||
        self.assertEqual(self.form.fields["creation_date"].initial, self.doc.date_of_creation)
 | 
			
		||||
        self.assertEqual(self.form.fields["file"].initial, self.doc.file)
 | 
			
		||||
 | 
			
		||||
    def test_save(self):
 | 
			
		||||
        self.assertTrue(self.form.is_valid(), msg=self.form.errors)
 | 
			
		||||
        obj = self.form.save()
 | 
			
		||||
        self.assertEqual(obj.title, self.data["title"])
 | 
			
		||||
        self.assertEqual(obj.comment, self.data["comment"])
 | 
			
		||||
        self.assertEqual(obj.date_of_creation, self.data["creation_date"])
 | 
			
		||||
 | 
			
		||||
        last_log = obj.instance.log.first()
 | 
			
		||||
        self.assertEqual(last_log.action, UserAction.EDITED)
 | 
			
		||||
        self.assertEqual(last_log.user, self.request.user)
 | 
			
		||||
        self.assertEqual(last_log.comment, DOCUMENT_EDITED)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RecordModalFormTestCase(BaseTestCase):
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
        self.request = RequestFactory().request()
 | 
			
		||||
        self.request.user = self.user
 | 
			
		||||
 | 
			
		||||
        self.fill_out_compensation(self.compensation)
 | 
			
		||||
 | 
			
		||||
    def test_init(self):
 | 
			
		||||
        form = RecordModalForm(
 | 
			
		||||
            request=self.request,
 | 
			
		||||
            instance=self.intervention
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(form.form_title, str(_("Record data")))
 | 
			
		||||
        self.assertEqual(form.form_caption, str(
 | 
			
		||||
            _("I, {} {}, confirm that all necessary control steps have been performed by myself.").format(
 | 
			
		||||
            self.user.first_name,
 | 
			
		||||
            self.user.last_name
 | 
			
		||||
            )
 | 
			
		||||
        ))
 | 
			
		||||
 | 
			
		||||
        self.intervention.set_recorded(self.user)
 | 
			
		||||
        self.intervention.refresh_from_db()
 | 
			
		||||
        form = RecordModalForm(
 | 
			
		||||
            request=self.request,
 | 
			
		||||
            instance=self.intervention
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(form.form_title, str(_("Unrecord data")))
 | 
			
		||||
        self.assertEqual(form.fields["confirm"].label, str(_("Confirm unrecord")))
 | 
			
		||||
        self.assertEqual(form.form_caption, str(
 | 
			
		||||
            _("I, {} {}, confirm that this data must be unrecorded.").format(
 | 
			
		||||
            self.user.first_name,
 | 
			
		||||
            self.user.last_name
 | 
			
		||||
        )))
 | 
			
		||||
 | 
			
		||||
    def test_is_valid(self):
 | 
			
		||||
        data = {
 | 
			
		||||
            "confirm": True
 | 
			
		||||
        }
 | 
			
		||||
        form = RecordModalForm(data, request=self.request, instance=self.intervention)
 | 
			
		||||
        self.assertFalse(self.intervention.is_recorded)
 | 
			
		||||
        self.assertFalse(form.is_valid(), msg=form.errors)  # intervention not complete
 | 
			
		||||
 | 
			
		||||
        self.intervention = self.fill_out_intervention(self.intervention)
 | 
			
		||||
        form = RecordModalForm(data, request=self.request, instance=self.intervention)
 | 
			
		||||
        self.assertTrue(form.is_valid(), msg=form.errors)
 | 
			
		||||
 | 
			
		||||
    def test_save(self):
 | 
			
		||||
        data = {
 | 
			
		||||
            "confirm": True
 | 
			
		||||
        }
 | 
			
		||||
        self.intervention = self.fill_out_intervention(self.intervention)
 | 
			
		||||
        form = RecordModalForm(data, request=self.request, instance=self.intervention)
 | 
			
		||||
        self.assertTrue(form.is_valid(), msg=form.errors)
 | 
			
		||||
 | 
			
		||||
        form.save()
 | 
			
		||||
        self.assertEqual(self.intervention.recorded.action, UserAction.RECORDED)
 | 
			
		||||
        self.assertEqual(self.intervention.recorded.user, self.request.user)
 | 
			
		||||
        self.assertEqual(self.intervention.recorded, self.intervention.log.first())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RemoveModalFormTestCase(BaseTestCase):
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
        self.request = RequestFactory().request()
 | 
			
		||||
        self.request.user = self.user
 | 
			
		||||
 | 
			
		||||
    def test_init(self):
 | 
			
		||||
        form = RemoveModalForm(request=self.request, instance=self.intervention)
 | 
			
		||||
        self.assertEqual(form.form_title, str(_("Remove")))
 | 
			
		||||
        self.assertEqual(form.form_caption, str(_("Are you sure?")))
 | 
			
		||||
        self.assertEqual(form.template, "modal/modal_form.html")
 | 
			
		||||
        self.assertEqual(form.request, self.request)
 | 
			
		||||
        self.assertEqual(form.user, self.request.user)
 | 
			
		||||
 | 
			
		||||
    def test_save(self):
 | 
			
		||||
        data = {
 | 
			
		||||
            "confirm": True,
 | 
			
		||||
        }
 | 
			
		||||
        form = RemoveModalForm(
 | 
			
		||||
            data,
 | 
			
		||||
            request=self.request,
 | 
			
		||||
            instance=self.intervention
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(form.is_valid(), msg=form.errors)
 | 
			
		||||
 | 
			
		||||
        form.save()
 | 
			
		||||
        self.assertEqual(self.intervention.deleted.action, UserAction.DELETED)
 | 
			
		||||
        self.assertEqual(self.intervention.deleted.user, self.request.user)
 | 
			
		||||
 | 
			
		||||
        payment = Payment.objects.create(
 | 
			
		||||
            amount=1.0,
 | 
			
		||||
            intervention=self.intervention
 | 
			
		||||
        )
 | 
			
		||||
        form = RemoveModalForm(
 | 
			
		||||
            data,
 | 
			
		||||
            request=self.request,
 | 
			
		||||
            instance=payment
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(form.is_valid(), msg=form.errors)
 | 
			
		||||
        form.save()
 | 
			
		||||
        try:
 | 
			
		||||
            payment.refresh_from_db()
 | 
			
		||||
            self.fail("Payment still exists")
 | 
			
		||||
        except ObjectDoesNotExist:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RemoveDeadlineTestCase(BaseTestCase):
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
        request = RequestFactory().request()
 | 
			
		||||
        request.user = self.user
 | 
			
		||||
        self.request = request
 | 
			
		||||
 | 
			
		||||
    def test_init(self):
 | 
			
		||||
        form = RemoveDeadlineModalForm(
 | 
			
		||||
            request=self.request,
 | 
			
		||||
            instance=self.compensation,
 | 
			
		||||
            deadline=self.finished_deadline
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(form.form_title, str(_("Remove")))
 | 
			
		||||
        self.assertEqual(form.form_caption, str(_("Are you sure?")))
 | 
			
		||||
        self.assertEqual(form.template, "modal/modal_form.html")
 | 
			
		||||
        self.assertEqual(form.request, self.request)
 | 
			
		||||
        self.assertEqual(form.user, self.request.user)
 | 
			
		||||
 | 
			
		||||
    def test_save(self):
 | 
			
		||||
        self.compensation.deadlines.add(self.finished_deadline)
 | 
			
		||||
        data = {
 | 
			
		||||
            "confirm": True
 | 
			
		||||
        }
 | 
			
		||||
        form = RemoveDeadlineModalForm(
 | 
			
		||||
            data,
 | 
			
		||||
            request=self.request,
 | 
			
		||||
            instance=self.compensation,
 | 
			
		||||
            deadline=self.finished_deadline
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(form.is_valid(), msg=form.errors)
 | 
			
		||||
        form.save()
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            self.finished_deadline.refresh_from_db()
 | 
			
		||||
            self.fail("Deadline still exists")
 | 
			
		||||
        except ObjectDoesNotExist:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        last_log = self.compensation.log.first()
 | 
			
		||||
        self.assertEqual(last_log.action, UserAction.EDITED)
 | 
			
		||||
        self.assertEqual(last_log.user, self.request.user)
 | 
			
		||||
        self.assertEqual(last_log.comment, DEADLINE_REMOVED)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ResubmissionModalFormTestCase(BaseTestCase):
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
        self.request = RequestFactory().request()
 | 
			
		||||
        self.request.user = self.user
 | 
			
		||||
 | 
			
		||||
    def test_init(self):
 | 
			
		||||
        # Resubmission nonexistent
 | 
			
		||||
        form = ResubmissionModalForm(request=self.request, instance=self.intervention)
 | 
			
		||||
        self.assertEqual(form.form_title, str(_("Resubmission")))
 | 
			
		||||
        self.assertEqual(form.form_caption, str(_("Set your resubmission for this entry.")))
 | 
			
		||||
        self.assertEqual(form.action_url, None)
 | 
			
		||||
        self.assertIsNotNone(form.resubmission)
 | 
			
		||||
 | 
			
		||||
        resubmission = Resubmission.objects.create(
 | 
			
		||||
            user=self.request.user,
 | 
			
		||||
            resubmit_on=now().date(),
 | 
			
		||||
            comment=generate_random_string(length=10, use_letters_lc=True)
 | 
			
		||||
        )
 | 
			
		||||
        self.intervention.resubmissions.add(resubmission)
 | 
			
		||||
 | 
			
		||||
        # Resubmission exists
 | 
			
		||||
        form = ResubmissionModalForm(request=self.request, instance=self.intervention)
 | 
			
		||||
        self.assertEqual(form.form_title, str(_("Resubmission")))
 | 
			
		||||
        self.assertEqual(form.form_caption, str(_("Set your resubmission for this entry.")))
 | 
			
		||||
        self.assertEqual(form.action_url, None)
 | 
			
		||||
        self.assertEqual(form.fields["date"].initial, str(resubmission.resubmit_on))
 | 
			
		||||
        self.assertEqual(form.fields["comment"].initial, resubmission.comment)
 | 
			
		||||
        self.assertEqual(form.resubmission, resubmission)
 | 
			
		||||
 | 
			
		||||
    def test_is_valid(self):
 | 
			
		||||
        yesterday = now().date() - timedelta(days=1)
 | 
			
		||||
        data = {
 | 
			
		||||
            "date": yesterday,
 | 
			
		||||
            "comment": "Edited comment"
 | 
			
		||||
        }
 | 
			
		||||
        form = ResubmissionModalForm(
 | 
			
		||||
            data,
 | 
			
		||||
            request=self.request,
 | 
			
		||||
            instance=self.intervention
 | 
			
		||||
        )
 | 
			
		||||
        self.assertFalse(form.is_valid(), msg=form.errors)
 | 
			
		||||
        self.assertTrue(form.has_error("date"))
 | 
			
		||||
 | 
			
		||||
        tomorrow = yesterday + timedelta(days=2)
 | 
			
		||||
        data = {
 | 
			
		||||
            "date": tomorrow,
 | 
			
		||||
            "comment": "Edited comment"
 | 
			
		||||
        }
 | 
			
		||||
        form = ResubmissionModalForm(
 | 
			
		||||
            data,
 | 
			
		||||
            request=self.request,
 | 
			
		||||
            instance=self.intervention
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(form.is_valid(), msg=form.errors)
 | 
			
		||||
 | 
			
		||||
    def test_save(self):
 | 
			
		||||
        data = {
 | 
			
		||||
            "date": now().date() + timedelta(days=1),
 | 
			
		||||
            "comment": "New comment for new resubmission"
 | 
			
		||||
        }
 | 
			
		||||
        form = ResubmissionModalForm(
 | 
			
		||||
            data,
 | 
			
		||||
            request=self.request,
 | 
			
		||||
            instance=self.intervention
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(form.is_valid(), msg=form.errors)
 | 
			
		||||
        obj = form.save()
 | 
			
		||||
        self.assertIn(obj, self.intervention.resubmissions.all())
 | 
			
		||||
        self.assertEqual(obj.resubmit_on, data["date"])
 | 
			
		||||
        self.assertEqual(obj.comment, data["comment"])
 | 
			
		||||
        self.assertEqual(obj.resubmission_sent, False)
 | 
			
		||||
        self.assertEqual(obj.user, self.request.user)
 | 
			
		||||
							
								
								
									
										201
									
								
								konova/tests/unit/test_models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								konova/tests/unit/test_models.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,201 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: ksp-servicestelle@sgdnord.rlp.de
 | 
			
		||||
Created on: 08.09.23
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
from django.test import RequestFactory
 | 
			
		||||
from django.utils.timezone import now
 | 
			
		||||
 | 
			
		||||
from intervention.forms.modals.share import ShareModalForm
 | 
			
		||||
from konova.models import DeadlineType, Resubmission
 | 
			
		||||
from konova.settings import ZB_GROUP
 | 
			
		||||
from konova.tests.test_views import BaseTestCase
 | 
			
		||||
from user.models import UserAction
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DeadlineTestCase(BaseTestCase):
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
 | 
			
		||||
    def test_str(self):
 | 
			
		||||
        self.assertEqual(str(self.finished_deadline), self.finished_deadline.type)
 | 
			
		||||
 | 
			
		||||
    def test_type_humanized_property(self):
 | 
			
		||||
        self.assertEqual(self.finished_deadline.type_humanized, DeadlineType.FINISHED.label)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BaseObjectTestCase(BaseTestCase):
 | 
			
		||||
    def test_add_log_entry(self):
 | 
			
		||||
        self.assertEqual(self.intervention.log.count(), 0)
 | 
			
		||||
        self.intervention.add_log_entry(UserAction.EDITED, self.user, "TEST")
 | 
			
		||||
        self.assertEqual(self.intervention.log.count(), 1)
 | 
			
		||||
        last_log = self.intervention.log.first()
 | 
			
		||||
        self.assertEqual(last_log.user, self.user)
 | 
			
		||||
        self.assertEqual(last_log.comment, "TEST")
 | 
			
		||||
        self.assertEqual(last_log.action, UserAction.EDITED)
 | 
			
		||||
 | 
			
		||||
    def test_generate_new_identifier(self):
 | 
			
		||||
        old_identifier = self.intervention.identifier
 | 
			
		||||
        new_identifier = self.intervention.generate_new_identifier()
 | 
			
		||||
        self.assertNotEqual(old_identifier, new_identifier)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RecordableObjectMixinTestCase(BaseTestCase):
 | 
			
		||||
    def test_set_recorded_and_set_unrecorded(self):
 | 
			
		||||
        """ Tests set_unrecorded() as well
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        self.intervention.set_recorded(self.user)
 | 
			
		||||
        self.assertIsNotNone(self.intervention.recorded)
 | 
			
		||||
        self.assertEqual(self.intervention.recorded.user, self.user)
 | 
			
		||||
        self.assertEqual(self.intervention.recorded.action, UserAction.RECORDED)
 | 
			
		||||
 | 
			
		||||
        self.intervention.set_unrecorded(self.user)
 | 
			
		||||
        self.assertIsNone(self.intervention.recorded)
 | 
			
		||||
        last_log = self.intervention.log.first()
 | 
			
		||||
        self.assertEqual(last_log.action, UserAction.UNRECORDED)
 | 
			
		||||
        self.assertEqual(last_log.user, self.user)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CheckableObjectMixinTestCase(BaseTestCase):
 | 
			
		||||
    def test_set_unchecked_and_set_checked(self):
 | 
			
		||||
        self.intervention.set_checked(self.user)
 | 
			
		||||
        self.assertIsNotNone(self.intervention.checked)
 | 
			
		||||
        self.assertEqual(self.intervention.checked.action, UserAction.CHECKED)
 | 
			
		||||
        self.assertEqual(self.intervention.checked.user, self.user)
 | 
			
		||||
        checked_action = self.intervention.checked
 | 
			
		||||
 | 
			
		||||
        self.intervention.set_unchecked()
 | 
			
		||||
        self.assertIsNone(self.intervention.checked)
 | 
			
		||||
 | 
			
		||||
        # There is no explicit UNCHECKED UserAction since unchecking does never happen manually but only as an
 | 
			
		||||
        # automatic consequence of editing an already checked entry. Therefore the last log entry in this case would
 | 
			
		||||
        # be the checking of the entry
 | 
			
		||||
        last_log = self.intervention.log.first()
 | 
			
		||||
        self.assertEqual(last_log.action, UserAction.CHECKED)
 | 
			
		||||
        self.assertEqual(last_log.user, self.user)
 | 
			
		||||
        self.assertEqual(last_log, checked_action)
 | 
			
		||||
 | 
			
		||||
    def test_get_last_checked_action(self):
 | 
			
		||||
        self.intervention.set_checked(self.user)
 | 
			
		||||
        action = self.intervention.checked
 | 
			
		||||
 | 
			
		||||
        self.intervention.mark_as_edited(self.user)
 | 
			
		||||
        last_log = self.intervention.log.first()
 | 
			
		||||
        self.assertNotEqual(last_log, action)
 | 
			
		||||
 | 
			
		||||
        last_check_action = self.intervention.get_last_checked_action()
 | 
			
		||||
        self.assertEqual(action, last_check_action)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ShareableObjectMixinTestCase(BaseTestCase):
 | 
			
		||||
    def test_share_with_and_is_shared_with(self):
 | 
			
		||||
        self.assertFalse(self.intervention.is_shared_with(self.user))
 | 
			
		||||
        self.assertNotIn(self.user, self.intervention.shared_users)
 | 
			
		||||
 | 
			
		||||
        self.intervention.share_with_user(self.user)
 | 
			
		||||
        self.assertTrue(self.intervention.is_shared_with(self.user))
 | 
			
		||||
        self.assertIn(self.user, self.intervention.shared_users)
 | 
			
		||||
 | 
			
		||||
        self.assertTrue(self.intervention.is_only_shared_with(self.user))
 | 
			
		||||
        self.assertFalse(self.intervention.is_only_shared_with(self.superuser))
 | 
			
		||||
        self.assertNotIn(self.superuser, self.intervention.shared_users)
 | 
			
		||||
        self.intervention.share_with_user(self.superuser)
 | 
			
		||||
        self.assertFalse(self.intervention.is_only_shared_with(self.user))
 | 
			
		||||
        self.assertIn(self.superuser, self.intervention.shared_users)
 | 
			
		||||
 | 
			
		||||
        self.intervention.share_with_user_list([])
 | 
			
		||||
        self.assertNotIn(self.superuser, self.intervention.shared_users)
 | 
			
		||||
        self.assertNotIn(self.user, self.intervention.shared_users)
 | 
			
		||||
        self.intervention.share_with_user_list([
 | 
			
		||||
            self.superuser,
 | 
			
		||||
            self.user
 | 
			
		||||
        ])
 | 
			
		||||
        self.assertIn(self.superuser, self.intervention.shared_users)
 | 
			
		||||
        self.assertIn(self.user, self.intervention.shared_users)
 | 
			
		||||
 | 
			
		||||
    def test_share_with_team_and_team_list(self):
 | 
			
		||||
        self.assertNotIn(self.team, self.intervention.shared_teams)
 | 
			
		||||
        self.intervention.share_with_team(self.team)
 | 
			
		||||
        self.assertIn(self.team, self.intervention.shared_teams)
 | 
			
		||||
 | 
			
		||||
        another_team = self.create_dummy_team(name="Another team")
 | 
			
		||||
        team_list = [
 | 
			
		||||
            self.team,
 | 
			
		||||
            another_team
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertNotIn(another_team, self.intervention.shared_teams)
 | 
			
		||||
        self.intervention.share_with_team_list(team_list)
 | 
			
		||||
        self.assertIn(another_team, self.intervention.shared_teams)
 | 
			
		||||
 | 
			
		||||
    def test_update_shared_access(self):
 | 
			
		||||
        another_team = self.create_dummy_team(name="Another team")
 | 
			
		||||
        request = RequestFactory().request()
 | 
			
		||||
        request.user = self.superuser
 | 
			
		||||
        self.superuser.groups.add(
 | 
			
		||||
            self.groups.get(name=ZB_GROUP)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.intervention.share_with_team(another_team)
 | 
			
		||||
        self.intervention.share_with_user(self.user)
 | 
			
		||||
        self.assertTrue(self.intervention.is_shared_with(self.user))
 | 
			
		||||
        self.assertIn(another_team, self.intervention.shared_teams)
 | 
			
		||||
 | 
			
		||||
        data = {
 | 
			
		||||
            "users": [
 | 
			
		||||
                self.superuser.id,
 | 
			
		||||
            ],
 | 
			
		||||
            "teams": [
 | 
			
		||||
                self.team.id,
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
        form = ShareModalForm(data, request=request, instance=self.intervention)
 | 
			
		||||
        self.assertTrue(form.is_valid(), msg=form.errors)
 | 
			
		||||
        form.save()
 | 
			
		||||
        self.assertNotIn(self.user, self.intervention.shared_users)
 | 
			
		||||
        self.assertNotIn(another_team, self.intervention.shared_teams)
 | 
			
		||||
        self.assertIn(self.superuser, self.intervention.shared_users)
 | 
			
		||||
        self.assertIn(self.team, self.intervention.shared_teams)
 | 
			
		||||
 | 
			
		||||
    def test_unshare_with_default_users(self):
 | 
			
		||||
        self.superuser.groups.add(
 | 
			
		||||
            self.groups.get(
 | 
			
		||||
                name=ZB_GROUP
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        self.intervention.share_with_user(self.user)
 | 
			
		||||
        self.intervention.share_with_user(self.superuser)
 | 
			
		||||
 | 
			
		||||
        self.assertTrue(self.user.is_default_group_only())
 | 
			
		||||
        self.assertFalse(self.superuser.is_default_group_only())
 | 
			
		||||
 | 
			
		||||
        self.assertTrue(self.intervention.is_shared_with(self.user))
 | 
			
		||||
        self.assertTrue(self.intervention.is_shared_with(self.superuser))
 | 
			
		||||
 | 
			
		||||
        self.intervention.unshare_with_default_users()
 | 
			
		||||
        self.assertFalse(self.intervention.is_shared_with(self.user))
 | 
			
		||||
        self.assertTrue(self.intervention.is_shared_with(self.superuser))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ResubmissionTestCase(BaseTestCase):
 | 
			
		||||
    def test_send_resubmission_mail(self):
 | 
			
		||||
        resubmission = Resubmission.objects.create(
 | 
			
		||||
            user=self.user,
 | 
			
		||||
            resubmit_on=now().date(),
 | 
			
		||||
            comment="Test",
 | 
			
		||||
        )
 | 
			
		||||
        self.intervention.resubmissions.add(resubmission)
 | 
			
		||||
 | 
			
		||||
        self.assertFalse(resubmission.resubmission_sent)
 | 
			
		||||
        resubmission.send_resubmission_mail(
 | 
			
		||||
            self.intervention.identifier,
 | 
			
		||||
            [
 | 
			
		||||
                "Test_municipal_1"
 | 
			
		||||
            ],
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(resubmission.resubmission_sent)
 | 
			
		||||
@ -19,17 +19,17 @@ from django.urls import path, include
 | 
			
		||||
 | 
			
		||||
from konova.settings import SSO_SERVER, SSO_PUBLIC_KEY, SSO_PRIVATE_KEY, DEBUG
 | 
			
		||||
from konova.sso.sso import KonovaSSOClient
 | 
			
		||||
from konova.views.logout import logout_view
 | 
			
		||||
from konova.views.geometry import get_geom_parcels, get_geom_parcels_content
 | 
			
		||||
from konova.views.home import home_view
 | 
			
		||||
from konova.views.logout import LogoutView
 | 
			
		||||
from konova.views.geometry import GeomParcelsView, GeomParcelsContentView
 | 
			
		||||
from konova.views.home import HomeView
 | 
			
		||||
from konova.views.map_proxy import ClientProxyParcelSearch, ClientProxyParcelWFS
 | 
			
		||||
 | 
			
		||||
sso_client = KonovaSSOClient(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('logout/', LogoutView.as_view(), name="logout"),
 | 
			
		||||
    path('', HomeView.as_view(), name="home"),
 | 
			
		||||
    path('intervention/', include("intervention.urls")),
 | 
			
		||||
    path('compensation/', include("compensation.urls")),
 | 
			
		||||
    path('ema/', include("ema.urls")),
 | 
			
		||||
@ -38,8 +38,8 @@ urlpatterns = [
 | 
			
		||||
    path('cl/', include("codelist.urls")),
 | 
			
		||||
    path('analysis/', include("analysis.urls")),
 | 
			
		||||
    path('api/', include("api.urls")),
 | 
			
		||||
    path('geom/<id>/parcels/', get_geom_parcels, name="geometry-parcels"),
 | 
			
		||||
    path('geom/<id>/parcels/<int:page>', get_geom_parcels_content, name="geometry-parcels-content"),
 | 
			
		||||
    path('geom/<id>/parcels/', GeomParcelsView.as_view(), name="geometry-parcels"),
 | 
			
		||||
    path('geom/<id>/parcels/<int:page>', GeomParcelsContentView.as_view(), name="geometry-parcels-content"),
 | 
			
		||||
    path('client/proxy', ClientProxyParcelSearch.as_view(), name="client-proxy-search"),
 | 
			
		||||
    path('client/proxy/wfs', ClientProxyParcelWFS.as_view(), name="client-proxy-wfs"),
 | 
			
		||||
]
 | 
			
		||||
@ -50,4 +50,4 @@ if DEBUG:
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
handler404 = "konova.views.error.get_404_view"
 | 
			
		||||
handler500 = "konova.views.error.get_500_view"
 | 
			
		||||
handler500 = "konova.views.error.get_500_view"
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,7 @@ INTERVENTION_INVALID = _("There are errors in this intervention.")
 | 
			
		||||
IDENTIFIER_REPLACED = _("The identifier '{}' had to be changed to '{}' since another entry has been added in the meanwhile, which uses this identifier")
 | 
			
		||||
ENTRY_REMOVE_MISSING_PERMISSION = _("Only conservation or registration office users are allowed to remove entries.")
 | 
			
		||||
MISSING_GROUP_PERMISSION = _("You need to be part of another user group.")
 | 
			
		||||
CHECKED_RECORDED_RESET = _("Status of Checked and Recorded reseted")
 | 
			
		||||
CHECK_STATE_RESET = _("Status of Checked reset")
 | 
			
		||||
RECORDED_BLOCKS_EDIT = _("Entry is recorded. To edit data, the entry first needs to be unrecorded.")
 | 
			
		||||
 | 
			
		||||
# SHARE
 | 
			
		||||
 | 
			
		||||
@ -1,37 +0,0 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: michel.peltriaux@sgdnord.rlp.de
 | 
			
		||||
Created on: 02.07.21
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
from user.models import User
 | 
			
		||||
 | 
			
		||||
from konova.settings import ETS_GROUP, ZB_GROUP
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def in_group(user: User, group: str) -> bool:
 | 
			
		||||
    """ Checks if the user is part of a group
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        user (User): The user object
 | 
			
		||||
        group (str): The group's name
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
        bool
 | 
			
		||||
    """
 | 
			
		||||
    return user.groups.filter(
 | 
			
		||||
        name=group
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def is_default_group_only(user: User) -> bool:
 | 
			
		||||
    """ Checks if the user is only part of the default group
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        user (User): The user object
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
        bool
 | 
			
		||||
    """
 | 
			
		||||
    return not in_group(user, ZB_GROUP) and not in_group(user, ETS_GROUP)
 | 
			
		||||
@ -5,104 +5,110 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
 | 
			
		||||
Created on: 19.08.22
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
from django.contrib.auth.mixins import LoginRequiredMixin
 | 
			
		||||
from django.contrib.gis.geos import MultiPolygon
 | 
			
		||||
from django.http import HttpResponse, HttpRequest
 | 
			
		||||
from django.shortcuts import get_object_or_404
 | 
			
		||||
from django.template.loader import render_to_string
 | 
			
		||||
from django.views import View
 | 
			
		||||
 | 
			
		||||
from konova.models import Geometry, Municipal
 | 
			
		||||
from konova.models import Geometry
 | 
			
		||||
from konova.sub_settings.lanis_settings import DEFAULT_SRID_RLP
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_geom_parcels(request: HttpRequest, id: str):
 | 
			
		||||
    """ Getter for HTMX
 | 
			
		||||
class GeomParcelsView(LoginRequiredMixin, View):
 | 
			
		||||
 | 
			
		||||
    Returns all parcels of the requested geometry rendered into a simple HTML table
 | 
			
		||||
    def get(self, request: HttpRequest, id: str):
 | 
			
		||||
        """ Getter for HTMX
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        request (HttpRequest): The incoming request
 | 
			
		||||
        id (str): The geometry's id
 | 
			
		||||
        Returns all parcels of the requested geometry rendered into a simple HTML table
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
        A rendered piece of HTML
 | 
			
		||||
    """
 | 
			
		||||
    # HTTP code 286 states that the HTMX should stop polling for updates
 | 
			
		||||
    # https://htmx.org/docs/#polling
 | 
			
		||||
    status_code = 286
 | 
			
		||||
    template = "konova/includes/parcels/parcel_table_frame.html"
 | 
			
		||||
    geom = get_object_or_404(Geometry, id=id)
 | 
			
		||||
    parcels = geom.get_underlying_parcels()
 | 
			
		||||
    geos_geom = geom.geom or MultiPolygon(srid=DEFAULT_SRID_RLP)
 | 
			
		||||
        Args:
 | 
			
		||||
            request (HttpRequest): The incoming request
 | 
			
		||||
            id (str): The geometry's id
 | 
			
		||||
 | 
			
		||||
    geometry_exists = not geos_geom.empty
 | 
			
		||||
    parcels_are_currently_calculated = geometry_exists and geos_geom.area > 0 and len(parcels) == 0
 | 
			
		||||
    parcels_available = len(parcels) > 0
 | 
			
		||||
        Returns:
 | 
			
		||||
            A rendered piece of HTML
 | 
			
		||||
        """
 | 
			
		||||
        # HTTP code 286 states that the HTMX should stop polling for updates
 | 
			
		||||
        # https://htmx.org/docs/#polling
 | 
			
		||||
        status_code = 286
 | 
			
		||||
        template = "konova/includes/parcels/parcel_table_frame.html"
 | 
			
		||||
        geom = get_object_or_404(Geometry, id=id)
 | 
			
		||||
        parcels = geom.get_underlying_parcels()
 | 
			
		||||
        geos_geom = geom.geom or MultiPolygon(srid=DEFAULT_SRID_RLP)
 | 
			
		||||
 | 
			
		||||
    if parcels_are_currently_calculated:
 | 
			
		||||
        # Parcels are being calculated right now. Change the status code, so polling stays active for fetching
 | 
			
		||||
        # resutls after the calculation
 | 
			
		||||
        status_code = 200
 | 
			
		||||
        geometry_exists = not geos_geom.empty
 | 
			
		||||
        parcels_are_currently_calculated = geometry_exists and geos_geom.area > 0 and len(parcels) == 0
 | 
			
		||||
        parcels_available = len(parcels) > 0
 | 
			
		||||
 | 
			
		||||
    if parcels_available or not geometry_exists:
 | 
			
		||||
        municipals = geom.get_underlying_municipals(parcels)
 | 
			
		||||
        if parcels_are_currently_calculated:
 | 
			
		||||
            # Parcels are being calculated right now. Change the status code, so polling stays active for fetching
 | 
			
		||||
            # resutls after the calculation
 | 
			
		||||
            status_code = 200
 | 
			
		||||
 | 
			
		||||
        if parcels_available or not geometry_exists:
 | 
			
		||||
            municipals = geom.get_underlying_municipals(parcels)
 | 
			
		||||
 | 
			
		||||
            rpp = 100
 | 
			
		||||
            num_all_parcels = parcels.count()
 | 
			
		||||
            parcels = parcels[:rpp]
 | 
			
		||||
            next_page = 1
 | 
			
		||||
            if len(parcels) < rpp:
 | 
			
		||||
                next_page = None
 | 
			
		||||
 | 
			
		||||
            context = {
 | 
			
		||||
                "num_parcels": num_all_parcels,
 | 
			
		||||
                "parcels": parcels,
 | 
			
		||||
                "municipals": municipals,
 | 
			
		||||
                "geom_id": str(id),
 | 
			
		||||
                "next_page": next_page,
 | 
			
		||||
            }
 | 
			
		||||
            html = render_to_string(template, context, request)
 | 
			
		||||
            return HttpResponse(html, status=status_code)
 | 
			
		||||
        else:
 | 
			
		||||
            return HttpResponse(None, status=404)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GeomParcelsContentView(LoginRequiredMixin, View):
 | 
			
		||||
 | 
			
		||||
    def get(self, request: HttpRequest, id: str, page: int):
 | 
			
		||||
        """ Getter for infinite scroll of HTMX
 | 
			
		||||
 | 
			
		||||
        Returns parcels of a specific page/slice of the found parcel set.
 | 
			
		||||
        Implementation of infinite scroll htmx example: https://htmx.org/examples/infinite-scroll/
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            request (HttpRequest): The incoming request
 | 
			
		||||
            id (str): The geometry's id
 | 
			
		||||
            page (int): The requested page number
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            A rendered piece of HTML
 | 
			
		||||
        """
 | 
			
		||||
        if page < 0:
 | 
			
		||||
            raise AssertionError("Parcel page can not be negative")
 | 
			
		||||
 | 
			
		||||
        # HTTP code 286 states that the HTMX should stop polling for updates
 | 
			
		||||
        # https://htmx.org/docs/#polling
 | 
			
		||||
        status_code = 286
 | 
			
		||||
        template = "konova/includes/parcels/parcel_table_content.html"
 | 
			
		||||
        geom = get_object_or_404(Geometry, id=id)
 | 
			
		||||
        parcels = geom.get_underlying_parcels()
 | 
			
		||||
 | 
			
		||||
        parcels = parcels.order_by("-municipal", "flr", "flrstck_zhlr", "flrstck_nnr")
 | 
			
		||||
        rpp = 100
 | 
			
		||||
        num_all_parcels = parcels.count()
 | 
			
		||||
        parcels = parcels[:rpp]
 | 
			
		||||
        next_page = 1
 | 
			
		||||
        from_p = rpp * (page-1)
 | 
			
		||||
        to_p = rpp * (page)
 | 
			
		||||
        next_page = page + 1
 | 
			
		||||
        parcels = parcels[from_p:to_p]
 | 
			
		||||
        if len(parcels) < rpp:
 | 
			
		||||
            next_page = None
 | 
			
		||||
 | 
			
		||||
        context = {
 | 
			
		||||
            "num_parcels": num_all_parcels,
 | 
			
		||||
            "parcels": parcels,
 | 
			
		||||
            "municipals": municipals,
 | 
			
		||||
            "geom_id": str(id),
 | 
			
		||||
            "next_page": next_page,
 | 
			
		||||
        }
 | 
			
		||||
        html = render_to_string(template, context, request)
 | 
			
		||||
        return HttpResponse(html, status=status_code)
 | 
			
		||||
    else:
 | 
			
		||||
        return HttpResponse(None, status=404)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_geom_parcels_content(request: HttpRequest, id: str, page: int):
 | 
			
		||||
    """ Getter for infinite scroll of HTMX
 | 
			
		||||
 | 
			
		||||
    Returns parcels of a specific page/slice of the found parcel set.
 | 
			
		||||
    Implementation of infinite scroll htmx example: https://htmx.org/examples/infinite-scroll/
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        request (HttpRequest): The incoming request
 | 
			
		||||
        id (str): The geometry's id
 | 
			
		||||
        page (int): The requested page number
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
        A rendered piece of HTML
 | 
			
		||||
    """
 | 
			
		||||
    if page < 0:
 | 
			
		||||
        raise AssertionError("Parcel page can not be negative")
 | 
			
		||||
 | 
			
		||||
    # HTTP code 286 states that the HTMX should stop polling for updates
 | 
			
		||||
    # https://htmx.org/docs/#polling
 | 
			
		||||
    status_code = 286
 | 
			
		||||
    template = "konova/includes/parcels/parcel_table_content.html"
 | 
			
		||||
    geom = get_object_or_404(Geometry, id=id)
 | 
			
		||||
    parcels = geom.get_underlying_parcels()
 | 
			
		||||
 | 
			
		||||
    parcels = parcels.order_by("-municipal", "flr", "flrstck_zhlr", "flrstck_nnr")
 | 
			
		||||
    rpp = 100
 | 
			
		||||
    from_p = rpp * (page-1)
 | 
			
		||||
    to_p = rpp * (page)
 | 
			
		||||
    next_page = page + 1
 | 
			
		||||
    parcels = parcels[from_p:to_p]
 | 
			
		||||
    if len(parcels) < rpp:
 | 
			
		||||
        next_page = None
 | 
			
		||||
 | 
			
		||||
    context = {
 | 
			
		||||
        "parcels": parcels,
 | 
			
		||||
        "geom_id": str(id),
 | 
			
		||||
        "next_page": next_page,
 | 
			
		||||
    }
 | 
			
		||||
    html = render_to_string(template, context, request)
 | 
			
		||||
    return HttpResponse(html, status=status_code)
 | 
			
		||||
 | 
			
		||||
@ -5,12 +5,13 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
 | 
			
		||||
Created on: 19.08.22
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
from django.contrib.auth.decorators import login_required
 | 
			
		||||
from django.contrib.auth.mixins import LoginRequiredMixin
 | 
			
		||||
from django.db.models import Q
 | 
			
		||||
from django.http import HttpRequest
 | 
			
		||||
from django.shortcuts import render
 | 
			
		||||
from django.utils import timezone
 | 
			
		||||
from django.utils.decorators import method_decorator
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
from django.views import View
 | 
			
		||||
 | 
			
		||||
from compensation.models import EcoAccount, Compensation
 | 
			
		||||
from intervention.models import Intervention
 | 
			
		||||
@ -20,59 +21,59 @@ from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
 | 
			
		||||
from news.models import ServerMessage
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@login_required
 | 
			
		||||
@any_group_check
 | 
			
		||||
def home_view(request: HttpRequest):
 | 
			
		||||
    """
 | 
			
		||||
    Renders the landing page
 | 
			
		||||
class HomeView(LoginRequiredMixin, View):
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        request (HttpRequest): The used request object
 | 
			
		||||
    @method_decorator(any_group_check)
 | 
			
		||||
    def get(self, request: HttpRequest):
 | 
			
		||||
        """
 | 
			
		||||
        Renders the landing page
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
        A redirect
 | 
			
		||||
    """
 | 
			
		||||
    template = "konova/home.html"
 | 
			
		||||
    now = timezone.now()
 | 
			
		||||
    user = request.user
 | 
			
		||||
    user_teams = user.shared_teams
 | 
			
		||||
        Args:
 | 
			
		||||
            request (HttpRequest): The used request object
 | 
			
		||||
 | 
			
		||||
    # Fetch the four newest active and published ServerMessages
 | 
			
		||||
    msgs = ServerMessage.get_current_news()[:3]
 | 
			
		||||
        Returns:
 | 
			
		||||
            A redirect
 | 
			
		||||
        """
 | 
			
		||||
        template = "konova/home.html"
 | 
			
		||||
        user = request.user
 | 
			
		||||
        user_teams = user.shared_teams
 | 
			
		||||
 | 
			
		||||
    # First fetch all valid objects (undeleted, only newest versions)
 | 
			
		||||
    interventions = Intervention.objects.filter(
 | 
			
		||||
        deleted=None,
 | 
			
		||||
    )
 | 
			
		||||
    # Then fetch only user related ones
 | 
			
		||||
    user_interventions = interventions.filter(
 | 
			
		||||
        Q(users__in=[user]) | Q(teams__in=user_teams)
 | 
			
		||||
    ).distinct()
 | 
			
		||||
        # Fetch the four newest active and published ServerMessages
 | 
			
		||||
        msgs = ServerMessage.get_current_news()[:3]
 | 
			
		||||
 | 
			
		||||
    # Repeat for other objects
 | 
			
		||||
    comps = Compensation.objects.filter(
 | 
			
		||||
        deleted=None,
 | 
			
		||||
    )
 | 
			
		||||
    user_comps = comps.filter(
 | 
			
		||||
        Q(intervention__users__in=[user]) | Q(intervention__teams__in=user_teams)
 | 
			
		||||
    ).distinct()
 | 
			
		||||
    eco_accs = EcoAccount.objects.filter(
 | 
			
		||||
        deleted=None,
 | 
			
		||||
    )
 | 
			
		||||
    user_ecco_accs = eco_accs.filter(
 | 
			
		||||
        Q(users__in=[user]) | Q(teams__in=user_teams)
 | 
			
		||||
    ).distinct()
 | 
			
		||||
        # First fetch all valid objects (undeleted, only newest versions)
 | 
			
		||||
        interventions = Intervention.objects.filter(
 | 
			
		||||
            deleted=None,
 | 
			
		||||
        )
 | 
			
		||||
        # Then fetch only user related ones
 | 
			
		||||
        user_interventions = interventions.filter(
 | 
			
		||||
            Q(users__in=[user]) | Q(teams__in=user_teams)
 | 
			
		||||
        ).distinct()
 | 
			
		||||
 | 
			
		||||
    additional_context = {
 | 
			
		||||
        "msgs": msgs,
 | 
			
		||||
        "total_intervention_count": interventions.count(),
 | 
			
		||||
        "user_intervention_count": user_interventions.count(),
 | 
			
		||||
        "total_compensation_count": comps.count(),
 | 
			
		||||
        "user_compensation_count": user_comps.count(),
 | 
			
		||||
        "total_eco_count": eco_accs.count(),
 | 
			
		||||
        "user_eco_count": user_ecco_accs.count(),
 | 
			
		||||
        TAB_TITLE_IDENTIFIER: _("Home"),
 | 
			
		||||
    }
 | 
			
		||||
    context = BaseContext(request, additional_context).context
 | 
			
		||||
    return render(request, template, context)
 | 
			
		||||
        # Repeat for other objects
 | 
			
		||||
        comps = Compensation.objects.filter(
 | 
			
		||||
            deleted=None,
 | 
			
		||||
        )
 | 
			
		||||
        user_comps = comps.filter(
 | 
			
		||||
            Q(intervention__users__in=[user]) | Q(intervention__teams__in=user_teams)
 | 
			
		||||
        ).distinct()
 | 
			
		||||
        eco_accs = EcoAccount.objects.filter(
 | 
			
		||||
            deleted=None,
 | 
			
		||||
        )
 | 
			
		||||
        user_ecco_accs = eco_accs.filter(
 | 
			
		||||
            Q(users__in=[user]) | Q(teams__in=user_teams)
 | 
			
		||||
        ).distinct()
 | 
			
		||||
 | 
			
		||||
        additional_context = {
 | 
			
		||||
            "msgs": msgs,
 | 
			
		||||
            "total_intervention_count": interventions.count(),
 | 
			
		||||
            "user_intervention_count": user_interventions.count(),
 | 
			
		||||
            "total_compensation_count": comps.count(),
 | 
			
		||||
            "user_compensation_count": user_comps.count(),
 | 
			
		||||
            "total_eco_count": eco_accs.count(),
 | 
			
		||||
            "user_eco_count": user_ecco_accs.count(),
 | 
			
		||||
            TAB_TITLE_IDENTIFIER: _("Home"),
 | 
			
		||||
        }
 | 
			
		||||
        context = BaseContext(request, additional_context).context
 | 
			
		||||
        return render(request, template, context)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -8,19 +8,21 @@ Created on: 19.08.22
 | 
			
		||||
from django.contrib.auth import logout
 | 
			
		||||
from django.http import HttpRequest
 | 
			
		||||
from django.shortcuts import redirect
 | 
			
		||||
from django.views import View
 | 
			
		||||
 | 
			
		||||
from konova.sub_settings.sso_settings import SSO_SERVER_BASE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def logout_view(request: HttpRequest):
 | 
			
		||||
    """
 | 
			
		||||
    Logout route for ending the session manually.
 | 
			
		||||
class LogoutView(View):
 | 
			
		||||
    def get(self, request: HttpRequest):
 | 
			
		||||
        """
 | 
			
		||||
        Logout route for ending the session manually.
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        request (HttpRequest): The used request object
 | 
			
		||||
        Args:
 | 
			
		||||
            request (HttpRequest): The used request object
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
        A redirect
 | 
			
		||||
    """
 | 
			
		||||
    logout(request)
 | 
			
		||||
    return redirect(SSO_SERVER_BASE)
 | 
			
		||||
        Returns:
 | 
			
		||||
            A redirect
 | 
			
		||||
        """
 | 
			
		||||
        logout(request)
 | 
			
		||||
        return redirect(SSO_SERVER_BASE)
 | 
			
		||||
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							@ -29,21 +29,21 @@
 | 
			
		||||
#: konova/filters/mixins/office.py:25 konova/filters/mixins/office.py:56
 | 
			
		||||
#: konova/filters/mixins/office.py:57 konova/filters/mixins/record.py:23
 | 
			
		||||
#: konova/filters/mixins/self_created.py:24 konova/filters/mixins/share.py:23
 | 
			
		||||
#: konova/forms/geometry_form.py:33 konova/forms/modals/document_form.py:26
 | 
			
		||||
#: konova/forms/geometry_form.py:32 konova/forms/modals/document_form.py:26
 | 
			
		||||
#: konova/forms/modals/document_form.py:36
 | 
			
		||||
#: konova/forms/modals/document_form.py:50
 | 
			
		||||
#: konova/forms/modals/document_form.py:62
 | 
			
		||||
#: konova/forms/modals/document_form.py:80
 | 
			
		||||
#: konova/forms/modals/remove_form.py:23
 | 
			
		||||
#: konova/forms/modals/resubmission_form.py:22
 | 
			
		||||
#: konova/forms/modals/resubmission_form.py:38 konova/forms/remove_form.py:19
 | 
			
		||||
#: user/forms/user.py:39
 | 
			
		||||
#: konova/forms/modals/resubmission_form.py:38 konova/forms/remove_form.py:25
 | 
			
		||||
#: konova/tests/unit/test_forms.py:59 user/forms/user.py:39
 | 
			
		||||
#, fuzzy
 | 
			
		||||
msgid ""
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Project-Id-Version: PACKAGE VERSION\n"
 | 
			
		||||
"Report-Msgid-Bugs-To: \n"
 | 
			
		||||
"POT-Creation-Date: 2023-08-29 10:29+0200\n"
 | 
			
		||||
"POT-Creation-Date: 2023-09-08 11:30+0200\n"
 | 
			
		||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 | 
			
		||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 | 
			
		||||
"Language-Team: LANGUAGE <LL@li.org>\n"
 | 
			
		||||
@ -96,15 +96,16 @@ msgstr "Verantwortliche Stelle"
 | 
			
		||||
msgid "Click for selection"
 | 
			
		||||
msgstr "Auswählen..."
 | 
			
		||||
 | 
			
		||||
#: analysis/forms.py:70
 | 
			
		||||
#: analysis/forms.py:70 analysis/tests/unit/test_forms.py:25
 | 
			
		||||
msgid "Generate report"
 | 
			
		||||
msgstr "Bericht generieren"
 | 
			
		||||
 | 
			
		||||
#: analysis/forms.py:71
 | 
			
		||||
#: analysis/forms.py:71 analysis/tests/unit/test_forms.py:26
 | 
			
		||||
msgid "Select a timespan and the desired conservation office"
 | 
			
		||||
msgstr "Wählen Sie die Zeitspanne und die gewünschte Eintragungsstelle"
 | 
			
		||||
 | 
			
		||||
#: analysis/forms.py:74 konova/forms/modals/base_form.py:30
 | 
			
		||||
#: analysis/forms.py:74 analysis/tests/unit/test_forms.py:29
 | 
			
		||||
#: konova/forms/modals/base_form.py:30
 | 
			
		||||
msgid "Continue"
 | 
			
		||||
msgstr "Weiter"
 | 
			
		||||
 | 
			
		||||
@ -231,6 +232,12 @@ msgstr "Andere Zulassungsbehörden"
 | 
			
		||||
msgid "Compensations"
 | 
			
		||||
msgstr "Kompensationen"
 | 
			
		||||
 | 
			
		||||
#: analysis/templates/analysis/reports/includes/compensation/card_compensation.html:13
 | 
			
		||||
#: analysis/templates/analysis/reports/includes/eco_account/card_eco_account.html:13
 | 
			
		||||
#: analysis/templates/analysis/reports/includes/intervention/card_intervention.html:12
 | 
			
		||||
msgid "Binding date after"
 | 
			
		||||
msgstr "Bestandskraft- bzw. Rechtskraftdatum nach"
 | 
			
		||||
 | 
			
		||||
#: analysis/templates/analysis/reports/includes/eco_account/card_eco_account.html:11
 | 
			
		||||
msgid "Eco-Accounts"
 | 
			
		||||
msgstr "Ökokonten"
 | 
			
		||||
@ -345,11 +352,11 @@ msgstr "Eingriff"
 | 
			
		||||
msgid "Eco-account"
 | 
			
		||||
msgstr "Ökokonto"
 | 
			
		||||
 | 
			
		||||
#: analysis/templates/analysis/reports/includes/old_data/card_old_interventions.html:11
 | 
			
		||||
#: analysis/templates/analysis/reports/includes/old_data/card_old_data.html:11
 | 
			
		||||
msgid "Old interventions"
 | 
			
		||||
msgstr "Altfälle"
 | 
			
		||||
 | 
			
		||||
#: analysis/templates/analysis/reports/includes/old_data/card_old_interventions.html:13
 | 
			
		||||
#: analysis/templates/analysis/reports/includes/old_data/card_old_data.html:13
 | 
			
		||||
msgid "Binding date before"
 | 
			
		||||
msgstr "Bestandskraft- bzw. Rechtskraftdatum vor"
 | 
			
		||||
 | 
			
		||||
@ -394,6 +401,7 @@ msgid "An explanatory name"
 | 
			
		||||
msgstr "Aussagekräftiger Titel"
 | 
			
		||||
 | 
			
		||||
#: compensation/forms/compensation.py:49 ema/forms.py:51 ema/forms.py:114
 | 
			
		||||
#: ema/tests/unit/test_forms.py:31 ema/tests/unit/test_forms.py:85
 | 
			
		||||
msgid "Compensation XY; Location ABC"
 | 
			
		||||
msgstr "Kompensation XY; Flur ABC"
 | 
			
		||||
 | 
			
		||||
@ -478,7 +486,15 @@ msgstr "Ökokonto XY; Flur ABC"
 | 
			
		||||
msgid "Edit Eco-Account"
 | 
			
		||||
msgstr "Ökokonto bearbeiten"
 | 
			
		||||
 | 
			
		||||
#: compensation/forms/eco_account.py:232
 | 
			
		||||
#: compensation/forms/eco_account.py:183
 | 
			
		||||
msgid ""
 | 
			
		||||
"{}m² have been deducted from this eco account so far. The given value of {} "
 | 
			
		||||
"would be too low."
 | 
			
		||||
msgstr ""
 | 
			
		||||
"{}n² wurden bereits von diesem Ökokonto abgebucht. Der eingegebene Wert von "
 | 
			
		||||
"{} wäre daher zu klein."
 | 
			
		||||
 | 
			
		||||
#: compensation/forms/eco_account.py:249
 | 
			
		||||
msgid "The account can not be removed, since there are still deductions."
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Das Ökokonto kann nicht entfernt werden, da hierzu noch Abbuchungen "
 | 
			
		||||
@ -591,16 +607,19 @@ msgid "Insert the amount"
 | 
			
		||||
msgstr "Menge eingeben"
 | 
			
		||||
 | 
			
		||||
#: compensation/forms/modals/compensation_action.py:94
 | 
			
		||||
#: compensation/tests/compensation/unit/test_forms.py:42
 | 
			
		||||
msgid "New action"
 | 
			
		||||
msgstr "Neue Maßnahme"
 | 
			
		||||
 | 
			
		||||
#: compensation/forms/modals/compensation_action.py:95
 | 
			
		||||
#: compensation/tests/compensation/unit/test_forms.py:43
 | 
			
		||||
msgid "Insert data for the new action"
 | 
			
		||||
msgstr "Geben Sie die Daten der neuen Maßnahme ein"
 | 
			
		||||
 | 
			
		||||
#: compensation/forms/modals/compensation_action.py:119
 | 
			
		||||
#: compensation/templates/compensation/detail/compensation/includes/actions.html:68
 | 
			
		||||
#: compensation/templates/compensation/detail/eco_account/includes/actions.html:67
 | 
			
		||||
#: compensation/tests/compensation/unit/test_forms.py:84
 | 
			
		||||
#: ema/templates/ema/detail/includes/actions.html:65
 | 
			
		||||
msgid "Edit action"
 | 
			
		||||
msgstr "Maßnahme bearbeiten"
 | 
			
		||||
@ -634,18 +653,21 @@ msgid "Additional comment, maximum {} letters"
 | 
			
		||||
msgstr "Zusätzlicher Kommentar, maximal {} Zeichen"
 | 
			
		||||
 | 
			
		||||
#: compensation/forms/modals/deadline.py:65
 | 
			
		||||
#: konova/tests/unit/test_deadline.py:29
 | 
			
		||||
msgid "New deadline"
 | 
			
		||||
msgstr "Neue Frist"
 | 
			
		||||
 | 
			
		||||
#: compensation/forms/modals/deadline.py:66
 | 
			
		||||
#: konova/tests/unit/test_deadline.py:30
 | 
			
		||||
msgid "Insert data for the new deadline"
 | 
			
		||||
msgstr "Geben Sie die Daten der neuen Frist ein"
 | 
			
		||||
 | 
			
		||||
#: compensation/forms/modals/deadline.py:75
 | 
			
		||||
#: compensation/forms/modals/deadline.py:78
 | 
			
		||||
#: konova/tests/unit/test_deadline.py:57
 | 
			
		||||
msgid "Please explain this 'other' type of deadline."
 | 
			
		||||
msgstr "Bitte erklären Sie um welchen 'sonstigen' Termin es sich handelt."
 | 
			
		||||
 | 
			
		||||
#: compensation/forms/modals/deadline.py:92
 | 
			
		||||
#: compensation/forms/modals/deadline.py:95
 | 
			
		||||
#: compensation/templates/compensation/detail/compensation/includes/deadlines.html:64
 | 
			
		||||
#: compensation/templates/compensation/detail/eco_account/includes/deadlines.html:62
 | 
			
		||||
#: ema/templates/ema/detail/includes/deadlines.html:62
 | 
			
		||||
@ -700,10 +722,12 @@ msgid "in m²"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: compensation/forms/modals/state.py:72
 | 
			
		||||
#: compensation/tests/compensation/unit/test_forms.py:175
 | 
			
		||||
msgid "New state"
 | 
			
		||||
msgstr "Neuer Zustand"
 | 
			
		||||
 | 
			
		||||
#: compensation/forms/modals/state.py:73
 | 
			
		||||
#: compensation/tests/compensation/unit/test_forms.py:176
 | 
			
		||||
msgid "Insert data for the new state"
 | 
			
		||||
msgstr "Geben Sie die Daten des neuen Zustandes ein"
 | 
			
		||||
 | 
			
		||||
@ -716,6 +740,7 @@ msgstr "Objekt entfernt"
 | 
			
		||||
#: compensation/templates/compensation/detail/compensation/includes/states-before.html:62
 | 
			
		||||
#: compensation/templates/compensation/detail/eco_account/includes/states-after.html:62
 | 
			
		||||
#: compensation/templates/compensation/detail/eco_account/includes/states-before.html:62
 | 
			
		||||
#: compensation/tests/compensation/unit/test_forms.py:236
 | 
			
		||||
#: ema/templates/ema/detail/includes/states-after.html:60
 | 
			
		||||
#: ema/templates/ema/detail/includes/states-before.html:60
 | 
			
		||||
msgid "Edit state"
 | 
			
		||||
@ -749,21 +774,6 @@ msgstr ""
 | 
			
		||||
msgid "Pieces"
 | 
			
		||||
msgstr "Stück"
 | 
			
		||||
 | 
			
		||||
#: compensation/models/eco_account.py:62
 | 
			
		||||
msgid ""
 | 
			
		||||
"Deductable surface can not be larger than existing surfaces in after states"
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Die abbuchbare Fläche darf die Gesamtfläche der Zielzustände nicht "
 | 
			
		||||
"überschreiten"
 | 
			
		||||
 | 
			
		||||
#: compensation/models/eco_account.py:69
 | 
			
		||||
msgid ""
 | 
			
		||||
"Deductable surface can not be smaller than the sum of already existing "
 | 
			
		||||
"deductions. Please contact the responsible users for the deductions!"
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Es wurde bereits mehr Fläche abgebucht, als Sie nun als abbuchbar einstellen "
 | 
			
		||||
"wollen. Kontaktieren Sie die für die Abbuchungen verantwortlichen Nutzer!"
 | 
			
		||||
 | 
			
		||||
#: compensation/tables/compensation.py:33 compensation/tables/eco_account.py:34
 | 
			
		||||
#: ema/tables.py:36 intervention/tables.py:33
 | 
			
		||||
#: konova/filters/mixins/geo_reference.py:42
 | 
			
		||||
@ -926,6 +936,7 @@ msgstr "Öffentlicher Bericht"
 | 
			
		||||
#: ema/templates/ema/detail/includes/controls.html:15
 | 
			
		||||
#: intervention/templates/intervention/detail/includes/controls.html:15
 | 
			
		||||
#: konova/forms/modals/resubmission_form.py:51
 | 
			
		||||
#: konova/tests/unit/test_forms.py:302 konova/tests/unit/test_forms.py:316
 | 
			
		||||
#: templates/email/resubmission/resubmission.html:4
 | 
			
		||||
msgid "Resubmission"
 | 
			
		||||
msgstr "Wiedervorlage"
 | 
			
		||||
@ -988,7 +999,7 @@ msgstr "Dokumente"
 | 
			
		||||
#: compensation/templates/compensation/detail/eco_account/includes/documents.html:14
 | 
			
		||||
#: ema/templates/ema/detail/includes/documents.html:14
 | 
			
		||||
#: intervention/templates/intervention/detail/includes/documents.html:14
 | 
			
		||||
#: konova/forms/modals/document_form.py:79
 | 
			
		||||
#: konova/forms/modals/document_form.py:79 konova/tests/unit/test_forms.py:58
 | 
			
		||||
msgid "Add new document"
 | 
			
		||||
msgstr "Neues Dokument hinzufügen"
 | 
			
		||||
 | 
			
		||||
@ -1004,7 +1015,7 @@ msgstr "Erstellt"
 | 
			
		||||
#: compensation/templates/compensation/detail/eco_account/includes/documents.html:61
 | 
			
		||||
#: ema/templates/ema/detail/includes/documents.html:61
 | 
			
		||||
#: intervention/templates/intervention/detail/includes/documents.html:70
 | 
			
		||||
#: konova/forms/modals/document_form.py:141
 | 
			
		||||
#: konova/forms/modals/document_form.py:141 konova/tests/unit/test_forms.py:118
 | 
			
		||||
msgid "Edit document"
 | 
			
		||||
msgstr "Dokument bearbeiten"
 | 
			
		||||
 | 
			
		||||
@ -1178,6 +1189,7 @@ msgstr "weitere Nutzer"
 | 
			
		||||
#: ema/templates/ema/detail/includes/controls.html:18
 | 
			
		||||
#: intervention/forms/modals/share.py:63
 | 
			
		||||
#: intervention/templates/intervention/detail/includes/controls.html:18
 | 
			
		||||
#: intervention/tests/unit/test_forms.py:150
 | 
			
		||||
msgid "Share"
 | 
			
		||||
msgstr "Freigabe"
 | 
			
		||||
 | 
			
		||||
@ -1282,14 +1294,14 @@ msgstr "Daten zu den verantwortlichen Stellen"
 | 
			
		||||
msgid "Compensations - Overview"
 | 
			
		||||
msgstr "Kompensationen - Übersicht"
 | 
			
		||||
 | 
			
		||||
#: compensation/views/compensation/compensation.py:182
 | 
			
		||||
#: compensation/views/compensation/compensation.py:181
 | 
			
		||||
#: konova/utils/message_templates.py:40
 | 
			
		||||
msgid "Compensation {} edited"
 | 
			
		||||
msgstr "Kompensation {} bearbeitet"
 | 
			
		||||
 | 
			
		||||
#: compensation/views/compensation/compensation.py:197
 | 
			
		||||
#: compensation/views/eco_account/eco_account.py:171 ema/views/ema.py:231
 | 
			
		||||
#: intervention/views/intervention.py:253
 | 
			
		||||
#: compensation/views/compensation/compensation.py:196
 | 
			
		||||
#: compensation/views/eco_account/eco_account.py:173 ema/views/ema.py:231
 | 
			
		||||
#: intervention/views/intervention.py:252
 | 
			
		||||
msgid "Edit {}"
 | 
			
		||||
msgstr "Bearbeite {}"
 | 
			
		||||
 | 
			
		||||
@ -1307,19 +1319,19 @@ msgstr "Ökokonten - Übersicht"
 | 
			
		||||
msgid "Eco-Account {} added"
 | 
			
		||||
msgstr "Ökokonto {} hinzugefügt"
 | 
			
		||||
 | 
			
		||||
#: compensation/views/eco_account/eco_account.py:156
 | 
			
		||||
#: compensation/views/eco_account/eco_account.py:158
 | 
			
		||||
msgid "Eco-Account {} edited"
 | 
			
		||||
msgstr "Ökokonto {} bearbeitet"
 | 
			
		||||
 | 
			
		||||
#: compensation/views/eco_account/eco_account.py:285
 | 
			
		||||
#: compensation/views/eco_account/eco_account.py:287
 | 
			
		||||
msgid "Eco-account removed"
 | 
			
		||||
msgstr "Ökokonto entfernt"
 | 
			
		||||
 | 
			
		||||
#: ema/forms.py:42 ema/views/ema.py:102
 | 
			
		||||
#: ema/forms.py:42 ema/tests/unit/test_forms.py:27 ema/views/ema.py:102
 | 
			
		||||
msgid "New EMA"
 | 
			
		||||
msgstr "Neue EMA hinzufügen"
 | 
			
		||||
 | 
			
		||||
#: ema/forms.py:108
 | 
			
		||||
#: ema/forms.py:108 ema/tests/unit/test_forms.py:81
 | 
			
		||||
msgid "Edit EMA"
 | 
			
		||||
msgstr "Bearbeite EMA"
 | 
			
		||||
 | 
			
		||||
@ -1418,6 +1430,7 @@ msgid "Binding on"
 | 
			
		||||
msgstr "Datum Bestandskraft bzw. Rechtskraft"
 | 
			
		||||
 | 
			
		||||
#: intervention/forms/intervention.py:216
 | 
			
		||||
#: intervention/tests/unit/test_forms.py:36
 | 
			
		||||
#: intervention/views/intervention.py:105
 | 
			
		||||
msgid "New intervention"
 | 
			
		||||
msgstr "Neuer Eingriff"
 | 
			
		||||
@ -1440,6 +1453,7 @@ msgid "Run check"
 | 
			
		||||
msgstr "Prüfung vornehmen"
 | 
			
		||||
 | 
			
		||||
#: intervention/forms/modals/check.py:36 konova/forms/modals/record_form.py:30
 | 
			
		||||
#: konova/tests/unit/test_forms.py:155
 | 
			
		||||
msgid ""
 | 
			
		||||
"I, {} {}, confirm that all necessary control steps have been performed by "
 | 
			
		||||
"myself."
 | 
			
		||||
@ -1502,6 +1516,7 @@ msgstr "Muss kleiner als 15 Mb sein"
 | 
			
		||||
 | 
			
		||||
#: intervention/forms/modals/revocation.py:62
 | 
			
		||||
#: intervention/templates/intervention/detail/includes/revocation.html:18
 | 
			
		||||
#: intervention/tests/unit/test_forms.py:234
 | 
			
		||||
msgid "Add revocation"
 | 
			
		||||
msgstr "Widerspruch hinzufügen"
 | 
			
		||||
 | 
			
		||||
@ -1543,6 +1558,7 @@ msgstr ""
 | 
			
		||||
"noch nicht freigegeben wurde. Geben Sie den ganzen Nutzernamen an."
 | 
			
		||||
 | 
			
		||||
#: intervention/forms/modals/share.py:64
 | 
			
		||||
#: intervention/tests/unit/test_forms.py:151
 | 
			
		||||
msgid "Share settings for {}"
 | 
			
		||||
msgstr "Freigabe Einstellungen für {}"
 | 
			
		||||
 | 
			
		||||
@ -1658,11 +1674,11 @@ msgstr "Eingriffe - Übersicht"
 | 
			
		||||
msgid "Intervention {} added"
 | 
			
		||||
msgstr "Eingriff {} hinzugefügt"
 | 
			
		||||
 | 
			
		||||
#: intervention/views/intervention.py:236
 | 
			
		||||
#: intervention/views/intervention.py:235
 | 
			
		||||
msgid "Intervention {} edited"
 | 
			
		||||
msgstr "Eingriff {} bearbeitet"
 | 
			
		||||
 | 
			
		||||
#: intervention/views/intervention.py:278
 | 
			
		||||
#: intervention/views/intervention.py:277
 | 
			
		||||
msgid "{} removed"
 | 
			
		||||
msgstr "{} entfernt"
 | 
			
		||||
 | 
			
		||||
@ -1780,12 +1796,12 @@ msgstr "Speichern"
 | 
			
		||||
msgid "Not editable"
 | 
			
		||||
msgstr "Nicht editierbar"
 | 
			
		||||
 | 
			
		||||
#: konova/forms/geometry_form.py:32 konova/utils/quality.py:44
 | 
			
		||||
#: konova/forms/geometry_form.py:31 konova/utils/quality.py:44
 | 
			
		||||
#: konova/utils/quality.py:46 templates/form/collapsable/form.html:45
 | 
			
		||||
msgid "Geometry"
 | 
			
		||||
msgstr "Geometrie"
 | 
			
		||||
 | 
			
		||||
#: konova/forms/geometry_form.py:101
 | 
			
		||||
#: konova/forms/geometry_form.py:100
 | 
			
		||||
msgid "Only surfaces allowed. Points or lines must be buffered."
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Nur Flächen erlaubt. Punkte oder Linien müssen zu Flächen gepuffert werden."
 | 
			
		||||
@ -1803,7 +1819,7 @@ msgstr "Datei"
 | 
			
		||||
msgid "Allowed formats: pdf, jpg, png. Max size 15 MB."
 | 
			
		||||
msgstr "Formate: pdf, jpg, png. Maximal 15 MB."
 | 
			
		||||
 | 
			
		||||
#: konova/forms/modals/document_form.py:116
 | 
			
		||||
#: konova/forms/modals/document_form.py:116 konova/tests/unit/test_forms.py:95
 | 
			
		||||
msgid "Added document"
 | 
			
		||||
msgstr "Dokument hinzugefügt"
 | 
			
		||||
 | 
			
		||||
@ -1811,32 +1827,34 @@ msgstr "Dokument hinzugefügt"
 | 
			
		||||
msgid "Confirm record"
 | 
			
		||||
msgstr "Verzeichnen bestätigen"
 | 
			
		||||
 | 
			
		||||
#: konova/forms/modals/record_form.py:29
 | 
			
		||||
#: konova/forms/modals/record_form.py:29 konova/tests/unit/test_forms.py:153
 | 
			
		||||
msgid "Record data"
 | 
			
		||||
msgstr "Daten verzeichnen"
 | 
			
		||||
 | 
			
		||||
#: konova/forms/modals/record_form.py:36
 | 
			
		||||
#: konova/forms/modals/record_form.py:36 konova/tests/unit/test_forms.py:168
 | 
			
		||||
msgid "Confirm unrecord"
 | 
			
		||||
msgstr "Entzeichnen bestätigen"
 | 
			
		||||
 | 
			
		||||
#: konova/forms/modals/record_form.py:37
 | 
			
		||||
#: konova/forms/modals/record_form.py:37 konova/tests/unit/test_forms.py:167
 | 
			
		||||
msgid "Unrecord data"
 | 
			
		||||
msgstr "Daten entzeichnen"
 | 
			
		||||
 | 
			
		||||
#: konova/forms/modals/record_form.py:38
 | 
			
		||||
#: konova/forms/modals/record_form.py:38 konova/tests/unit/test_forms.py:170
 | 
			
		||||
msgid "I, {} {}, confirm that this data must be unrecorded."
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Ich, {} {}, bestätige, dass diese Daten wieder entzeichnet werden müssen."
 | 
			
		||||
 | 
			
		||||
#: konova/forms/modals/remove_form.py:22 konova/forms/remove_form.py:18
 | 
			
		||||
#: konova/forms/modals/remove_form.py:22 konova/forms/remove_form.py:24
 | 
			
		||||
msgid "Confirm"
 | 
			
		||||
msgstr "Bestätige"
 | 
			
		||||
 | 
			
		||||
#: konova/forms/modals/remove_form.py:32 konova/forms/remove_form.py:30
 | 
			
		||||
#: konova/forms/modals/remove_form.py:32 konova/forms/remove_form.py:36
 | 
			
		||||
#: konova/tests/unit/test_forms.py:209 konova/tests/unit/test_forms.py:261
 | 
			
		||||
msgid "Remove"
 | 
			
		||||
msgstr "Löschen"
 | 
			
		||||
 | 
			
		||||
#: konova/forms/modals/remove_form.py:33
 | 
			
		||||
#: konova/forms/modals/remove_form.py:33 konova/tests/unit/test_forms.py:210
 | 
			
		||||
#: konova/tests/unit/test_forms.py:262
 | 
			
		||||
msgid "Are you sure?"
 | 
			
		||||
msgstr "Sind Sie sicher?"
 | 
			
		||||
 | 
			
		||||
@ -1845,6 +1863,7 @@ msgid "When do you want to be reminded?"
 | 
			
		||||
msgstr "Wann wollen Sie erinnert werden?"
 | 
			
		||||
 | 
			
		||||
#: konova/forms/modals/resubmission_form.py:52
 | 
			
		||||
#: konova/tests/unit/test_forms.py:303 konova/tests/unit/test_forms.py:317
 | 
			
		||||
msgid "Set your resubmission for this entry."
 | 
			
		||||
msgstr "Setzen Sie eine Wiedervorlage für diesen Eintrag."
 | 
			
		||||
 | 
			
		||||
@ -1852,7 +1871,7 @@ msgstr "Setzen Sie eine Wiedervorlage für diesen Eintrag."
 | 
			
		||||
msgid "The date should be in the future"
 | 
			
		||||
msgstr "Das Datum sollte in der Zukunft liegen"
 | 
			
		||||
 | 
			
		||||
#: konova/forms/remove_form.py:32
 | 
			
		||||
#: konova/forms/remove_form.py:38
 | 
			
		||||
msgid "You are about to remove {} {}"
 | 
			
		||||
msgstr "Sie sind dabei {} {} zu löschen"
 | 
			
		||||
 | 
			
		||||
@ -2067,8 +2086,8 @@ msgid "You need to be part of another user group."
 | 
			
		||||
msgstr "Hierfür müssen Sie einer anderen Nutzergruppe angehören!"
 | 
			
		||||
 | 
			
		||||
#: konova/utils/message_templates.py:21
 | 
			
		||||
msgid "Status of Checked and Recorded reseted"
 | 
			
		||||
msgstr "'Geprüft'/'Verzeichnet' wurde zurückgesetzt"
 | 
			
		||||
msgid "Status of Checked reset"
 | 
			
		||||
msgstr "Status 'Geprüft' wurde zurückgesetzt"
 | 
			
		||||
 | 
			
		||||
#: konova/utils/message_templates.py:22
 | 
			
		||||
msgid ""
 | 
			
		||||
@ -2227,15 +2246,11 @@ msgstr "Dokument bearbeitet"
 | 
			
		||||
msgid "Edited general data"
 | 
			
		||||
msgstr "Allgemeine Daten bearbeitet"
 | 
			
		||||
 | 
			
		||||
#: konova/utils/message_templates.py:82
 | 
			
		||||
msgid "Added deadline"
 | 
			
		||||
msgstr "Frist/Termin hinzugefügt"
 | 
			
		||||
 | 
			
		||||
#: konova/utils/message_templates.py:85
 | 
			
		||||
#: konova/utils/message_templates.py:84
 | 
			
		||||
msgid "Geometry conflict detected with {}"
 | 
			
		||||
msgstr "Geometriekonflikt mit folgenden Einträgen erkannt: {}"
 | 
			
		||||
 | 
			
		||||
#: konova/utils/message_templates.py:86
 | 
			
		||||
#: konova/utils/message_templates.py:85
 | 
			
		||||
msgid ""
 | 
			
		||||
"The geometry contained more than {} vertices. It had to be simplified to "
 | 
			
		||||
"match the allowed limit of {} vertices."
 | 
			
		||||
@ -2243,20 +2258,20 @@ msgstr ""
 | 
			
		||||
"Die Geometrie enthielt mehr als {} Eckpunkte. Sie musste vereinfacht werden "
 | 
			
		||||
"um die Obergrenze von {} erlaubten Eckpunkten einzuhalten."
 | 
			
		||||
 | 
			
		||||
#: konova/utils/message_templates.py:89
 | 
			
		||||
#: konova/utils/message_templates.py:88
 | 
			
		||||
msgid "This intervention has {} revocations"
 | 
			
		||||
msgstr "Dem Eingriff liegen {} Widersprüche vor"
 | 
			
		||||
 | 
			
		||||
#: konova/utils/message_templates.py:92
 | 
			
		||||
#: konova/utils/message_templates.py:91
 | 
			
		||||
msgid "Checked on {} by {}"
 | 
			
		||||
msgstr "Am {} von {} geprüft worden"
 | 
			
		||||
 | 
			
		||||
#: konova/utils/message_templates.py:93
 | 
			
		||||
#: konova/utils/message_templates.py:92
 | 
			
		||||
msgid "Data has changed since last check on {} by {}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Daten wurden nach der letzten Prüfung geändert. Letzte Prüfung am {} durch {}"
 | 
			
		||||
 | 
			
		||||
#: konova/utils/message_templates.py:94
 | 
			
		||||
#: konova/utils/message_templates.py:93
 | 
			
		||||
msgid "Current data not checked yet"
 | 
			
		||||
msgstr "Momentane Daten noch nicht geprüft"
 | 
			
		||||
 | 
			
		||||
@ -2290,7 +2305,7 @@ msgstr ""
 | 
			
		||||
"Dieses Datum ist unrealistisch. Geben Sie bitte das korrekte Datum ein "
 | 
			
		||||
"(>1950)."
 | 
			
		||||
 | 
			
		||||
#: konova/views/home.py:74 templates/navbars/navbar.html:16
 | 
			
		||||
#: konova/views/home.py:75 templates/navbars/navbar.html:16
 | 
			
		||||
msgid "Home"
 | 
			
		||||
msgstr "Home"
 | 
			
		||||
 | 
			
		||||
@ -4607,6 +4622,24 @@ msgstr ""
 | 
			
		||||
msgid "Unable to connect to qpid with SASL mechanism %s"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#~ msgid ""
 | 
			
		||||
#~ "Deductable surface can not be larger than existing surfaces in after "
 | 
			
		||||
#~ "states"
 | 
			
		||||
#~ msgstr ""
 | 
			
		||||
#~ "Die abbuchbare Fläche darf die Gesamtfläche der Zielzustände nicht "
 | 
			
		||||
#~ "überschreiten"
 | 
			
		||||
 | 
			
		||||
#~ msgid ""
 | 
			
		||||
#~ "Deductable surface can not be smaller than the sum of already existing "
 | 
			
		||||
#~ "deductions. Please contact the responsible users for the deductions!"
 | 
			
		||||
#~ msgstr ""
 | 
			
		||||
#~ "Es wurde bereits mehr Fläche abgebucht, als Sie nun als abbuchbar "
 | 
			
		||||
#~ "einstellen wollen. Kontaktieren Sie die für die Abbuchungen "
 | 
			
		||||
#~ "verantwortlichen Nutzer!"
 | 
			
		||||
 | 
			
		||||
#~ msgid "Added deadline"
 | 
			
		||||
#~ msgstr "Frist/Termin hinzugefügt"
 | 
			
		||||
 | 
			
		||||
#~ msgid "Change default configuration for your KSP map"
 | 
			
		||||
#~ msgstr "Karteneinstellungen ändern"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -60,6 +60,29 @@ class User(AbstractUser):
 | 
			
		||||
            name=ETS_GROUP
 | 
			
		||||
        ).exists()
 | 
			
		||||
 | 
			
		||||
    def is_default_group_only(self) -> bool:
 | 
			
		||||
        """ Checks if the user is only part of the default group
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            bool
 | 
			
		||||
        """
 | 
			
		||||
        return not self.in_group(ZB_GROUP) and not self.in_group(ETS_GROUP)
 | 
			
		||||
 | 
			
		||||
    def in_group(self, group: str) -> bool:
 | 
			
		||||
        """ Checks if the user is part of a group
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            group (str): The group's name
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            bool
 | 
			
		||||
        """
 | 
			
		||||
        return self.groups.filter(
 | 
			
		||||
            name=group
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def send_mail_shared_access_removed(self, obj_identifier, obj_title, municipals_names):
 | 
			
		||||
        """ Sends a mail to the user in case of removed shared access
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										7
									
								
								user/tests/unit/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								user/tests/unit/__init__.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: ksp-servicestelle@sgdnord.rlp.de
 | 
			
		||||
Created on: 12.09.23
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
							
								
								
									
										286
									
								
								user/tests/unit/test_forms.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										286
									
								
								user/tests/unit/test_forms.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,286 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: ksp-servicestelle@sgdnord.rlp.de
 | 
			
		||||
Created on: 12.09.23
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
from django.core.exceptions import ObjectDoesNotExist
 | 
			
		||||
from django.test import RequestFactory
 | 
			
		||||
from django.urls import reverse
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from api.models import APIUserToken
 | 
			
		||||
from konova.tests.test_views import BaseTestCase
 | 
			
		||||
from user.forms.modals.team import NewTeamModalForm, EditTeamModalForm, RemoveTeamModalForm, LeaveTeamModalForm
 | 
			
		||||
from user.forms.user import UserNotificationForm, UserAPITokenForm
 | 
			
		||||
from user.models import Team, UserAction, UserNotification
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NewTeamModalFormTestCase(BaseTestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
        self.request = RequestFactory().request()
 | 
			
		||||
        self.request.user = self.user
 | 
			
		||||
 | 
			
		||||
    def test_init(self):
 | 
			
		||||
        form = NewTeamModalForm(
 | 
			
		||||
            request=self.request
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(form.form_title, str(_("Create new team")))
 | 
			
		||||
        self.assertEqual(form.form_caption, str(_("You will become the administrator for this group by default. You do not need to add yourself to the list of members.")))
 | 
			
		||||
        self.assertEqual(form.action_url, reverse("user:team-new"))
 | 
			
		||||
        self.assertEqual(form.cancel_redirect, reverse("user:team-index"))
 | 
			
		||||
        self.assertEqual(form.request, self.request)
 | 
			
		||||
        self.assertEqual(form.user, self.request.user)
 | 
			
		||||
 | 
			
		||||
    def test_is_valid(self):
 | 
			
		||||
        invalid_data = {
 | 
			
		||||
            "name": self.team.name,
 | 
			
		||||
            "description": "Test description",
 | 
			
		||||
            "members": [self.superuser.id,],
 | 
			
		||||
        }
 | 
			
		||||
        form = NewTeamModalForm(
 | 
			
		||||
            invalid_data,
 | 
			
		||||
            request=self.request
 | 
			
		||||
        )
 | 
			
		||||
        self.assertFalse(form.is_valid())
 | 
			
		||||
        self.assertTrue(form.has_error("name"))
 | 
			
		||||
 | 
			
		||||
        valid_data = invalid_data
 | 
			
		||||
        valid_data["name"] = self.team.name + "_OTHER"
 | 
			
		||||
 | 
			
		||||
        form = NewTeamModalForm(
 | 
			
		||||
            invalid_data,
 | 
			
		||||
            request=self.request
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(form.is_valid())
 | 
			
		||||
 | 
			
		||||
    def test_save(self):
 | 
			
		||||
        valid_data = {
 | 
			
		||||
            "name": self.team.name + "_OTHER",
 | 
			
		||||
            "description": "Test description",
 | 
			
		||||
            "members": [self.superuser.id,],
 | 
			
		||||
        }
 | 
			
		||||
        form = NewTeamModalForm(
 | 
			
		||||
            valid_data,
 | 
			
		||||
            request=self.request
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(form.is_valid())
 | 
			
		||||
        obj = form.save()
 | 
			
		||||
        self.assertEqual(obj.name, valid_data["name"])
 | 
			
		||||
        self.assertEqual(obj.description, valid_data["description"])
 | 
			
		||||
        users = obj.users.all()
 | 
			
		||||
        admins = obj.admins.all()
 | 
			
		||||
        self.assertIn(self.request.user, users)
 | 
			
		||||
        self.assertIn(self.request.user, admins)
 | 
			
		||||
        self.assertIn(self.superuser, users)
 | 
			
		||||
        self.assertNotIn(self.superuser, admins)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EditTeamModalFormTestCase(NewTeamModalFormTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_init(self):
 | 
			
		||||
        self.team.admins.add(self.superuser)
 | 
			
		||||
 | 
			
		||||
        form = EditTeamModalForm(request=self.request, instance=self.team)
 | 
			
		||||
        self.assertEqual(form.form_title, str(_("Edit team")))
 | 
			
		||||
        self.assertEqual(form.action_url, reverse("user:team-edit", args=(self.team.id,)))
 | 
			
		||||
        self.assertEqual(form.cancel_redirect, reverse("user:team-index"))
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(form.fields["name"].initial, self.team.name)
 | 
			
		||||
        self.assertEqual(form.fields["description"].initial, self.team.description)
 | 
			
		||||
        self.assertEqual(form.fields["members"].initial.count(), 1)
 | 
			
		||||
        self.assertIn(self.superuser, form.fields["members"].initial)
 | 
			
		||||
        self.assertEqual(form.fields["admins"].initial.count(), 1)
 | 
			
		||||
        self.assertIn(self.superuser, form.fields["admins"].initial)
 | 
			
		||||
 | 
			
		||||
    def test_is_valid(self):
 | 
			
		||||
        data = {
 | 
			
		||||
            "name": self.team.name,
 | 
			
		||||
            "description": self.team.description,
 | 
			
		||||
            "members": self.team.users.values_list("id", flat=True),
 | 
			
		||||
            "admins": self.team.admins.values_list("id", flat=True),
 | 
			
		||||
        }
 | 
			
		||||
        form = EditTeamModalForm(
 | 
			
		||||
            data,
 | 
			
		||||
            request=self.request,
 | 
			
		||||
            instance=self.team
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # Error 1: Admin not in user list
 | 
			
		||||
        self.team.users.set([self.superuser])
 | 
			
		||||
        self.team.admins.set([self.user])
 | 
			
		||||
        self.assertFalse(form.is_valid())
 | 
			
		||||
        self.assertTrue(form.has_error("admins"))
 | 
			
		||||
 | 
			
		||||
        # Error 2: Admin list empty
 | 
			
		||||
        self.team.admins.set([])
 | 
			
		||||
        self.assertFalse(form.is_valid())
 | 
			
		||||
        self.assertTrue(form.has_error("admins"))
 | 
			
		||||
 | 
			
		||||
        # Error 3: Name taken
 | 
			
		||||
        other_team = Team.objects.create(
 | 
			
		||||
            name=self.team.name
 | 
			
		||||
        )
 | 
			
		||||
        self.team.admins.set([self.superuser])
 | 
			
		||||
        self.assertFalse(form.is_valid())
 | 
			
		||||
        self.assertTrue(form.has_error("name"))
 | 
			
		||||
 | 
			
		||||
    def test_save(self):
 | 
			
		||||
        data = {
 | 
			
		||||
            "name": self.team.name + "_EDITED",
 | 
			
		||||
            "description": self.team.description + "_EDITED",
 | 
			
		||||
            "members": [self.user.id, self.superuser.id,],
 | 
			
		||||
            "admins": [self.user.id,],
 | 
			
		||||
        }
 | 
			
		||||
        form = EditTeamModalForm(
 | 
			
		||||
            data,
 | 
			
		||||
            request=self.request,
 | 
			
		||||
            instance=self.team
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(form.is_valid(), msg=form.errors)
 | 
			
		||||
        obj = form.save()
 | 
			
		||||
        self.assertEqual(obj.name, data["name"])
 | 
			
		||||
        self.assertEqual(obj.description, data["description"])
 | 
			
		||||
        self.assertIn(self.user, obj.users.all())
 | 
			
		||||
        self.assertIn(self.superuser, obj.users.all())
 | 
			
		||||
        self.assertIn(self.user, obj.admins.all())
 | 
			
		||||
        self.assertEqual(obj.admins.count(), 1)
 | 
			
		||||
        self.assertEqual(obj.users.count(), 2)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RemoveTeamModalFormTestCase(BaseTestCase):
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
        self.request = RequestFactory().request()
 | 
			
		||||
        self.request.user = self.user
 | 
			
		||||
 | 
			
		||||
    def test_init(self):
 | 
			
		||||
        form = RemoveTeamModalForm(
 | 
			
		||||
            request=self.request,
 | 
			
		||||
            instance=self.team
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(form.form_caption, str(_("ATTENTION!\n\nRemoving the team means all members will lose their access to data, based on this team! \n\nAre you sure to remove this team?")))
 | 
			
		||||
        self.assertEqual(form.user, self.request.user)
 | 
			
		||||
        self.assertEqual(form.request, self.request)
 | 
			
		||||
 | 
			
		||||
    def test_save(self):
 | 
			
		||||
        data = {
 | 
			
		||||
            "confirm": True
 | 
			
		||||
        }
 | 
			
		||||
        form = RemoveTeamModalForm(
 | 
			
		||||
            data,
 | 
			
		||||
            request=self.request,
 | 
			
		||||
            instance=self.team
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(form.is_valid(), msg=form.errors)
 | 
			
		||||
        form.save()
 | 
			
		||||
        self.team.refresh_from_db()
 | 
			
		||||
 | 
			
		||||
        self.assertIsNotNone(self.team.deleted)
 | 
			
		||||
        self.assertEqual(self.team.deleted.user, self.request.user)
 | 
			
		||||
        self.assertEqual(self.team.deleted.action, UserAction.DELETED)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LeaveTeamModalFormTestCase(BaseTestCase):
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
        self.request = RequestFactory().request()
 | 
			
		||||
        self.request.user = self.user
 | 
			
		||||
 | 
			
		||||
    def test_init(self):
 | 
			
		||||
        form = LeaveTeamModalForm(
 | 
			
		||||
            request=self.request,
 | 
			
		||||
            instance=self.team
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(form.form_title, str(_("Leave team")))
 | 
			
		||||
 | 
			
		||||
    def test_save(self):
 | 
			
		||||
        self.team.users.add(self.user)
 | 
			
		||||
        data = {
 | 
			
		||||
            "confirm": True,
 | 
			
		||||
        }
 | 
			
		||||
        form = LeaveTeamModalForm(
 | 
			
		||||
            data,
 | 
			
		||||
            request=self.request,
 | 
			
		||||
            instance=self.team
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(form.is_valid(), msg=form.errors)
 | 
			
		||||
        self.assertIn(self.request.user, self.team.users.all())
 | 
			
		||||
        form.save()
 | 
			
		||||
        self.assertNotIn(self.request.user, self.team.users.all())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UserNotificationFormTestCase(BaseTestCase):
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
 | 
			
		||||
        if not UserNotification.objects.all().exists():
 | 
			
		||||
            self.notifications = UserNotification.objects.bulk_create(
 | 
			
		||||
                [
 | 
			
		||||
                    UserNotification(id="notification_1", name="notification_1", is_active=True),
 | 
			
		||||
                    UserNotification(id="notification_2", name="notification_2", is_active=True),
 | 
			
		||||
                    UserNotification(id="notification_3", name="notification_3", is_active=True),
 | 
			
		||||
                    UserNotification(id="notification_4", name="notification_4", is_active=True),
 | 
			
		||||
                ]
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    def test_init(self):
 | 
			
		||||
        form = UserNotificationForm(
 | 
			
		||||
            user=self.user
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(form.form_title, str(_("Edit notifications")))
 | 
			
		||||
        self.assertEqual(form.form_caption, "")
 | 
			
		||||
        self.assertEqual(form.action_url, reverse("user:notifications"))
 | 
			
		||||
        self.assertEqual(form.cancel_redirect, reverse("user:index"))
 | 
			
		||||
 | 
			
		||||
    def test_save(self):
 | 
			
		||||
        selected_notification = UserNotification.objects.first()
 | 
			
		||||
        data = {
 | 
			
		||||
            "notifications": [selected_notification.id,]
 | 
			
		||||
        }
 | 
			
		||||
        form = UserNotificationForm(
 | 
			
		||||
            data=data,
 | 
			
		||||
            user=self.user
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(form.is_valid(), msg=form.errors)
 | 
			
		||||
        self.assertEqual(self.user.notifications.count(), 0)
 | 
			
		||||
        form.save()
 | 
			
		||||
        self.assertEqual(self.user.notifications.count(), 1)
 | 
			
		||||
        self.assertIn(selected_notification, self.user.notifications.all())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UserAPITokenFormTestCase(BaseTestCase):
 | 
			
		||||
    def test_init(self):
 | 
			
		||||
        form = UserAPITokenForm(
 | 
			
		||||
            instance=self.user
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(form.form_title, str(_("Create new token")))
 | 
			
		||||
        self.assertEqual(form.form_caption, str(_("A new token needs to be validated by an administrator!")))
 | 
			
		||||
        self.assertEqual(form.action_url, reverse("user:api-token"))
 | 
			
		||||
        self.assertEqual(form.cancel_redirect, reverse("user:index"))
 | 
			
		||||
 | 
			
		||||
        self.assertIsNone(form.fields["token"].initial)
 | 
			
		||||
        self.assertTrue(form.fields["token"].widget.attrs["readonly"])
 | 
			
		||||
 | 
			
		||||
    def test_save(self):
 | 
			
		||||
        data = {
 | 
			
		||||
            "token": APIUserToken().token
 | 
			
		||||
        }
 | 
			
		||||
        form = UserAPITokenForm(
 | 
			
		||||
            data,
 | 
			
		||||
            instance=self.user
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(form.is_valid(), msg=form.errors)
 | 
			
		||||
        self.assertIsNone(self.user.api_token)
 | 
			
		||||
        token = form.save()
 | 
			
		||||
        self.assertEqual(self.user.api_token, token)
 | 
			
		||||
        new_token = form.save()
 | 
			
		||||
        self.assertEqual(self.user.api_token, new_token)
 | 
			
		||||
        try:
 | 
			
		||||
            token.refresh_from_db()
 | 
			
		||||
            self.fail("Token should be deleted and not be fetchable anymore")
 | 
			
		||||
        except ObjectDoesNotExist:
 | 
			
		||||
            pass
 | 
			
		||||
							
								
								
									
										61
									
								
								user/tests/unit/test_models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								user/tests/unit/test_models.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,61 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: ksp-servicestelle@sgdnord.rlp.de
 | 
			
		||||
Created on: 13.09.23
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
from konova.settings import ZB_GROUP, DEFAULT_GROUP, ETS_GROUP
 | 
			
		||||
from konova.tests.test_views import BaseTestCase
 | 
			
		||||
from user.enums import UserNotificationEnum
 | 
			
		||||
from user.models import UserNotification
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UserTestCase(BaseTestCase):
 | 
			
		||||
    def test_is_notification_setting_set(self):
 | 
			
		||||
        notification = UserNotification.objects.create(
 | 
			
		||||
            id=UserNotificationEnum.NOTIFY_ON_DEDUCTION_CHANGES.name,
 | 
			
		||||
            name=UserNotificationEnum.NOTIFY_ON_DEDUCTION_CHANGES.value,
 | 
			
		||||
        )
 | 
			
		||||
        self.assertFalse(self.user.is_notification_setting_set(UserNotificationEnum.NOTIFY_ON_DEDUCTION_CHANGES))
 | 
			
		||||
        self.user.notifications.add(notification)
 | 
			
		||||
        self.assertTrue(self.user.is_notification_setting_set(UserNotificationEnum.NOTIFY_ON_DEDUCTION_CHANGES))
 | 
			
		||||
 | 
			
		||||
    def test_is_group_member(self):
 | 
			
		||||
        zb_group = self.groups.get(name=ZB_GROUP)
 | 
			
		||||
        ets_group = self.groups.get(name=ETS_GROUP)
 | 
			
		||||
        default_group = self.groups.get(name=DEFAULT_GROUP)
 | 
			
		||||
 | 
			
		||||
        self.user.groups.set([])
 | 
			
		||||
        self.assertFalse(self.user.is_zb_user())
 | 
			
		||||
        self.assertFalse(self.user.is_ets_user())
 | 
			
		||||
        self.assertFalse(self.user.is_default_user())
 | 
			
		||||
 | 
			
		||||
        self.user.groups.add(zb_group)
 | 
			
		||||
        self.assertTrue(self.user.is_zb_user())
 | 
			
		||||
 | 
			
		||||
        self.user.groups.add(ets_group)
 | 
			
		||||
        self.assertTrue(self.user.is_ets_user())
 | 
			
		||||
 | 
			
		||||
        self.user.groups.add(default_group)
 | 
			
		||||
        self.assertTrue(self.user.is_default_user())
 | 
			
		||||
 | 
			
		||||
    def test_get_API_token(self):
 | 
			
		||||
        self.assertIsNone(self.user.api_token)
 | 
			
		||||
        token = self.user.get_API_token()
 | 
			
		||||
        self.assertIsNotNone(self.user.api_token)
 | 
			
		||||
        self.assertEqual(self.user.api_token, token)
 | 
			
		||||
 | 
			
		||||
        # Make sure the same token is returned if command is called twice
 | 
			
		||||
        token = self.user.get_API_token()
 | 
			
		||||
        self.assertEqual(self.user.api_token, token)
 | 
			
		||||
 | 
			
		||||
    def test_shared_teams_property(self):
 | 
			
		||||
        shared_teams = self.user.shared_teams
 | 
			
		||||
        self.assertEqual(shared_teams.count(), 0)
 | 
			
		||||
 | 
			
		||||
        self.team.users.add(self.user)
 | 
			
		||||
        shared_teams = self.user.shared_teams
 | 
			
		||||
        self.assertEqual(shared_teams.count(), 1)
 | 
			
		||||
        self.assertIn(self.team, shared_teams)
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user