diff --git a/compensation/models/compensation.py b/compensation/models/compensation.py index 3a238164..b2f35f39 100644 --- a/compensation/models/compensation.py +++ b/compensation/models/compensation.py @@ -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 @@ -296,6 +297,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) diff --git a/compensation/models/eco_account.py b/compensation/models/eco_account.py index 35e4c02b..b14f641c 100644 --- a/compensation/models/eco_account.py +++ b/compensation/models/eco_account.py @@ -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,6 +53,9 @@ 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})" diff --git a/ema/models/ema.py b/ema/models/ema.py index 8d627587..5e380eef 100644 --- a/ema/models/ema.py +++ b/ema/models/ema.py @@ -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) diff --git a/intervention/models/intervention.py b/intervention/models/intervention.py index face187f..22847f7b 100644 --- a/intervention/models/intervention.py +++ b/intervention/models/intervention.py @@ -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})" diff --git a/intervention/tests/unit/__init__.py b/intervention/tests/unit/__init__.py new file mode 100644 index 00000000..685f2583 --- /dev/null +++ b/intervention/tests/unit/__init__.py @@ -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 + +""" diff --git a/intervention/tests/unit/test_forms.py b/intervention/tests/unit/test_forms.py new file mode 100644 index 00000000..f50d6d15 --- /dev/null +++ b/intervention/tests/unit/test_forms.py @@ -0,0 +1,123 @@ +""" +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 django.urls import reverse +from django.utils.timezone import now +from django.utils.translation import gettext_lazy as _ + +from intervention.forms.intervention import NewInterventionForm, EditInterventionForm +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 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"]) diff --git a/konova/models/object.py b/konova/models/object.py index 4f996f5b..1fe0cebb 100644 --- a/konova/models/object.py +++ b/konova/models/object.py @@ -143,6 +143,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 +196,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_IDENTIFIER_LENGTH, - "template": EMA_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 +208,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):