Compare commits

...

3 Commits

Author SHA1 Message Date
da23761f88 #36 Quality checks
* adds check on recording of intervention in case of invalid compensations
2021-10-26 07:47:53 +02:00
4a4c9ad049 #36 Quality checks
* adds unchecking/unrecording of interventions in case of post-check|post-record editing
2021-10-25 17:39:39 +02:00
1c3ab898cc #36 Quality checks
* refactors toggling of recorded status into RecordableMixin
2021-10-25 17:01:02 +02:00
10 changed files with 182 additions and 56 deletions

View File

@ -22,7 +22,7 @@ from compensation.managers import CompensationStateManager, EcoAccountDeductionM
from compensation.utils.quality import CompensationQualityChecker, EcoAccountQualityChecker
from intervention.models import Intervention, ResponsibilityData, LegalData
from konova.models import BaseObject, BaseResource, Geometry, UuidModel, AbstractDocument, \
generate_document_file_upload_path
generate_document_file_upload_path, RecordableMixin
from konova.settings import DEFAULT_SRID_RLP, LANIS_LINK_TEMPLATE
from user.models import UserActionLogEntry
@ -221,11 +221,12 @@ class Compensation(AbstractCompensation):
def save(self, *args, **kwargs):
if self.identifier is None or len(self.identifier) == 0:
# Create new identifier
new_id = self.generate_new_identifier()
while Compensation.objects.filter(identifier=new_id).exists():
new_id = self.generate_new_identifier()
self.identifier = new_id
# Create new identifier is none was given
self.identifier = self.generate_new_identifier()
# Before saving, make sure a given identifier has not been taken already in the meanwhile
while Compensation.objects.filter(identifier=self.identifier).exclude(id=self.id).exists():
self.identifier = self.generate_new_identifier()
super().save(*args, **kwargs)
def get_LANIS_link(self) -> str:
@ -310,7 +311,7 @@ class CompensationDocument(AbstractDocument):
pass
class EcoAccount(AbstractCompensation):
class EcoAccount(AbstractCompensation, RecordableMixin):
"""
An eco account is a kind of 'prepaid' compensation. It can be compared to an account that already has been filled
with some kind of currency. From this account one is able to deduct currency for current projects.
@ -368,11 +369,12 @@ class EcoAccount(AbstractCompensation):
def save(self, *args, **kwargs):
if self.identifier is None or len(self.identifier) == 0:
# Create new identifier
new_id = self.generate_new_identifier()
while EcoAccount.objects.filter(identifier=new_id).exists():
new_id = self.generate_new_identifier()
self.identifier = new_id
# Create new identifier if none was given
self.identifier = self.generate_new_identifier()
# Before saving, make sure the given identifier is not used, yet
while EcoAccount.objects.filter(identifier=self.identifier).exclude(id=self.id).exists():
self.identifier = self.generate_new_identifier()
super().save(*args, **kwargs)
@property

View File

@ -7,12 +7,12 @@ from django.db.models import QuerySet
from compensation.models import AbstractCompensation
from ema.managers import EmaManager
from ema.utils.quality import EmaQualityChecker
from konova.models import AbstractDocument, generate_document_file_upload_path
from konova.models import AbstractDocument, generate_document_file_upload_path, RecordableMixin
from konova.settings import DEFAULT_SRID_RLP, LANIS_LINK_TEMPLATE
from user.models import UserActionLogEntry
class Ema(AbstractCompensation):
class Ema(AbstractCompensation, RecordableMixin):
"""
EMA = Ersatzzahlungsmaßnahme
(compensation actions from payments)

View File

@ -355,5 +355,9 @@ class EditInterventionForm(NewInterventionForm):
self.instance.modified = user_action
self.instance.save()
# Uncheck and unrecord intervention due to changed data
self.instance.set_unchecked(user)
self.instance.set_unrecorded(user)
return self.instance

View File

@ -10,7 +10,6 @@ import shutil
from django.contrib.auth.models import User
from django.contrib.gis.db import models
from django.db.models import QuerySet
from django.utils.translation import gettext_lazy as _
from codelist.models import KonovaCode
from codelist.settings import CODELIST_REGISTRATION_OFFICE_ID, CODELIST_CONSERVATION_OFFICE_ID, CODELIST_LAW_ID, \
@ -18,7 +17,7 @@ from codelist.settings import CODELIST_REGISTRATION_OFFICE_ID, CODELIST_CONSERVA
from intervention.managers import InterventionManager
from intervention.utils.quality import InterventionQualityChecker
from konova.models import BaseObject, Geometry, UuidModel, BaseResource, AbstractDocument, \
generate_document_file_upload_path
generate_document_file_upload_path, RecordableMixin, CheckableMixin
from konova.settings import DEFAULT_SRID_RLP, LANIS_LINK_TEMPLATE, LANIS_ZOOM_LUT
from konova.utils import generators
from user.models import UserActionLogEntry
@ -172,7 +171,7 @@ class LegalData(UuidModel):
revocation = models.OneToOneField(Revocation, null=True, blank=True, help_text="Refers to 'Widerspruch am'", on_delete=models.SET_NULL)
class Intervention(BaseObject):
class Intervention(BaseObject, RecordableMixin, CheckableMixin):
"""
Interventions are e.g. construction sites where nature used to be.
"""
@ -274,11 +273,11 @@ class Intervention(BaseObject):
"""
if self.identifier is None or len(self.identifier) == 0:
# No identifier given
# No identifier given by the user
self.identifier = self.generate_new_identifier()
# Before saving, make sure the set identifier is not used, yet
while Intervention.objects.filter(identifier=self.identifier).exists():
# Before saving, make sure the given identifier is not used in the meanwhile
while Intervention.objects.filter(identifier=self.identifier).exclude(id=self.id).exists():
self.identifier = self.generate_new_identifier()
super().save(*args, **kwargs)

View File

@ -15,7 +15,7 @@ from konova.sub_settings.django_settings import DEFAULT_DATE_FORMAT
from konova.utils.documents import remove_document, get_document
from konova.utils.generators import generate_qr_code
from konova.utils.message_templates import INTERVENTION_INVALID, FORM_INVALID, IDENTIFIER_REPLACED, \
DATA_UNSHARED_EXPLANATION
DATA_UNSHARED_EXPLANATION, CHECKED_RECORDED_RESET
from konova.utils.user_checks import in_group
@ -270,8 +270,13 @@ def edit_view(request: HttpRequest, id: str):
if request.method == "POST":
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 = 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)
return redirect("intervention:detail", id=intervention.id)
else:
messages.error(request, FORM_INVALID, extra_tags="danger",)

View File

@ -481,26 +481,29 @@ class RecordModalForm(BaseModalForm):
"confirm",
msg
)
# Special case: Intervention
# Add direct checks for related compensations
if isinstance(self.instance, Intervention):
self._are_compensations_valid()
return super_val and checker.valid
def _are_compensations_valid(self):
""" Runs a special case for intervention-compensations validity
Returns:
"""
comps = self.instance.compensations.all()
for comp in comps:
checker = comp.quality_check()
for msg in checker.messages:
self.add_error(
"confirm",
f"{comp.identifier}: {msg}"
)
def save(self):
with transaction.atomic():
if self.cleaned_data["confirm"]:
if self.instance.recorded:
# unrecord!
unrecord_action = UserActionLogEntry.objects.create(
user=self.user,
action=UserAction.UNRECORDED
)
# Do not delete the old .recorded attribute, since it shall stay in the .log list!
self.instance.recorded = None
self.instance.log.add(unrecord_action)
else:
record_action = UserActionLogEntry.objects.create(
user=self.user,
action=UserAction.RECORDED
)
self.instance.recorded = record_action
self.instance.log.add(record_action)
self.instance.save()
self.instance.toggle_recorded(self.user)
return self.instance

View File

@ -313,3 +313,107 @@ class Geometry(BaseResource):
"""
from konova.settings import DEFAULT_SRID
geom = MultiPolygonField(null=True, blank=True, srid=DEFAULT_SRID)
class RecordableMixin:
""" Mixin to be combined with BaseObject class
Provides functionality related to un/recording of data
"""
def set_unrecorded(self, user: User):
""" Perform unrecording
Args:
user (User): Performing user
Returns:
"""
action = UserActionLogEntry.objects.create(
user=user,
action=UserAction.UNRECORDED
)
self.recorded = None
self.save()
self.log.add(action)
def set_recorded(self, user: User):
""" Perform recording
Args:
user (User): Performing user
Returns:
"""
action = UserActionLogEntry.objects.create(
user=user,
action=UserAction.RECORDED
)
self.recorded = action
self.save()
self.log.add(action)
def toggle_recorded(self, user: User):
""" Un/Record intervention
Args:
user (User): Performing user
Returns:
"""
if not self.recorded:
self.set_recorded(user)
else:
self.set_unrecorded(user)
class CheckableMixin:
""" Mixin to be combined with BaseObject class
Provides functionality related to un/checking of data
"""
def set_unchecked(self, user: User):
""" Perform unrecording
Args:
Returns:
"""
self.checked = None
self.save()
def set_checked(self, user: User):
""" Perform checking
Args:
user (User): Performing user
Returns:
"""
action = UserActionLogEntry.objects.create(
user=user,
action=UserAction.CHECKED
)
self.checked = action
self.save()
self.log.add(action)
def toggle_checked(self, user: User):
""" Un/Record intervention
Args:
user (User): Performing user
Returns:
"""
if not self.checked:
self.set_checked(user)
else:
self.set_unchecked(user)

View File

@ -14,4 +14,6 @@ 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")
DATA_UNSHARED = _("This data is not shared with you")
DATA_UNSHARED_EXPLANATION = _("Remember: This data has not been shared with you, yet. This means you can only read but can not edit or perform any actions like running a check or recording.")
MISSING_GROUP_PERMISSION = _("You need to be part of another user group.")
MISSING_GROUP_PERMISSION = _("You need to be part of another user group.")
CHECKED_RECORDED_RESET = _("Status of Checked and Recorded reseted")

Binary file not shown.

View File

@ -19,7 +19,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-10-25 14:13+0200\n"
"POT-Creation-Date: 2021-10-25 17:10+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"
@ -41,7 +41,8 @@ msgstr "Bis"
#: compensation/templates/compensation/detail/eco_account/view.html:58
#: compensation/templates/compensation/report/eco_account/report.html:16
#: compensation/utils/quality.py:100 ema/templates/ema/detail/view.html:42
#: ema/templates/ema/report/report.html:16 intervention/forms/forms.py:101
#: ema/templates/ema/report/report.html:16 ema/utils/quality.py:26
#: intervention/forms/forms.py:101
#: intervention/templates/intervention/detail/view.html:56
#: intervention/templates/intervention/report/report.html:37
#: intervention/utils/quality.py:49
@ -373,7 +374,8 @@ msgstr "Zusätzlicher Kommentar"
#: compensation/templates/compensation/detail/eco_account/view.html:62
#: compensation/templates/compensation/report/eco_account/report.html:20
#: compensation/utils/quality.py:102 ema/templates/ema/detail/view.html:46
#: ema/templates/ema/report/report.html:20 intervention/forms/forms.py:129
#: ema/templates/ema/report/report.html:20 ema/utils/quality.py:28
#: intervention/forms/forms.py:129
#: intervention/templates/intervention/detail/view.html:60
#: intervention/templates/intervention/report/report.html:41
#: intervention/utils/quality.py:42
@ -1030,7 +1032,8 @@ msgstr ""
"Die abbuchbare Fläche darf die Gesamtfläche der Zielzustände nicht "
"überschreiten"
#: compensation/utils/quality.py:104 intervention/utils/quality.py:55
#: compensation/utils/quality.py:104 ema/utils/quality.py:30
#: intervention/utils/quality.py:55
msgid "Responsible data"
msgstr "Daten zu den verantwortlichen Stellen"
@ -1044,7 +1047,7 @@ msgstr "Kompensation {} bearbeitet"
#: compensation/views/compensation_views.py:216
#: compensation/views/eco_account_views.py:290 ema/views.py:178
#: intervention/views.py:447
#: intervention/views.py:448
msgid "Log"
msgstr "Log"
@ -1098,16 +1101,16 @@ msgid "Deduction removed"
msgstr "Abbuchung entfernt"
#: compensation/views/eco_account_views.py:310 ema/views.py:252
#: intervention/views.py:487
#: intervention/views.py:488
msgid "{} unrecorded"
msgstr "{} entzeichnet"
#: compensation/views/eco_account_views.py:310 ema/views.py:252
#: intervention/views.py:487
#: intervention/views.py:488
msgid "{} recorded"
msgstr "{} verzeichnet"
#: compensation/views/eco_account_views.py:455 intervention/views.py:469
#: compensation/views/eco_account_views.py:455 intervention/views.py:470
msgid "Deduction added"
msgstr "Abbuchung hinzugefügt"
@ -1436,39 +1439,43 @@ msgstr "Es existiert ein Widerspruch vom {}"
msgid "Intervention {} edited"
msgstr "Eingriff {} bearbeitet"
#: intervention/views.py:306
#: intervention/views.py:275
msgid "Status of Checked and Recorded reseted"
msgstr "'Geprüft' und 'Verzeichnet' sind zurückgesetzt worden"
#: intervention/views.py:307
msgid "{} removed"
msgstr "{} entfernt"
#: intervention/views.py:327
#: intervention/views.py:328
msgid "Revocation removed"
msgstr "Widerspruch entfernt"
#: intervention/views.py:353
#: intervention/views.py:354
msgid "{} has already been shared with you"
msgstr "{} wurde bereits für Sie freigegeben"
#: intervention/views.py:358
#: intervention/views.py:359
msgid "{} has been shared with you"
msgstr "{} ist nun für Sie freigegeben"
#: intervention/views.py:365
#: intervention/views.py:366
msgid "Share link invalid"
msgstr "Freigabelink ungültig"
#: intervention/views.py:386
#: intervention/views.py:387
msgid "Share settings updated"
msgstr "Freigabe Einstellungen aktualisiert"
#: intervention/views.py:405
#: intervention/views.py:406
msgid "Check performed"
msgstr "Prüfung durchgeführt"
#: intervention/views.py:425
#: intervention/views.py:426
msgid "Revocation added"
msgstr "Widerspruch hinzugefügt"
#: intervention/views.py:492
#: intervention/views.py:493
msgid "There are errors on this intervention:"
msgstr "Es liegen Fehler in diesem Eingriff vor:"