Merge pull request '35_DB_sanity_command' (#44) from 35_DB_sanity_command into master

Reviewed-on: SGD-Nord/konova#44
This commit is contained in:
Michel Peltriaux 2021-11-17 14:37:34 +01:00
commit 3e606c2a49
23 changed files with 664 additions and 355 deletions

View File

@ -18,6 +18,7 @@ from compensation.models import Compensation, EcoAccount
from intervention.inputs import GenerateInput
from intervention.models import Intervention, Responsibility, Legal
from konova.forms import BaseForm, SimpleGeomForm
from konova.utils.message_templates import EDITED_GENERAL_DATA
from user.models import UserActionLogEntry
@ -284,6 +285,8 @@ class EditCompensationForm(NewCompensationForm):
self.instance.save()
self.instance.log.add(action)
intervention.mark_as_edited(user, self.request, EDITED_GENERAL_DATA)
return self.instance

View File

@ -19,7 +19,8 @@ from compensation.models import CompensationDocument, EcoAccountDocument
from konova.contexts import BaseContext
from konova.forms import BaseModalForm, NewDocumentForm
from konova.models import DeadlineType
from konova.utils.message_templates import FORM_INVALID
from konova.utils.message_templates import FORM_INVALID, ADDED_COMPENSATION_STATE, ADDED_DEADLINE, \
ADDED_COMPENSATION_ACTION
class NewPaymentForm(BaseModalForm):
@ -98,6 +99,7 @@ class NewPaymentForm(BaseModalForm):
def save(self):
pay = self.instance.add_payment(self)
self.instance.mark_as_edited(self.user, self.request)
return pay
@ -147,6 +149,7 @@ class NewStateModalForm(BaseModalForm):
def save(self, is_before_state: bool = False):
state = self.instance.add_state(self, is_before_state)
self.instance.mark_as_edited(self.user, self.request, ADDED_COMPENSATION_STATE)
return state
def process_request(self, request: HttpRequest, msg_success: str = _("Object removed"), msg_error: str = FORM_INVALID, redirect_url: str = None):
@ -249,7 +252,8 @@ class NewDeadlineModalForm(BaseModalForm):
self.form_caption = _("Insert data for the new deadline")
def save(self):
deadline = self.instance.add_new_deadline(self)
deadline = self.instance.add_deadline(self)
self.instance.mark_as_edited(self.user, self.request, ADDED_DEADLINE)
return deadline
@ -325,7 +329,8 @@ class NewActionModalForm(BaseModalForm):
self.form_caption = _("Insert data for the new action")
def save(self):
action = self.instance.add_new_action(self)
action = self.instance.add_action(self)
self.instance.mark_as_edited(self.user, self.request, ADDED_COMPENSATION_ACTION)
return action

View File

@ -10,6 +10,7 @@ import shutil
from django.contrib.auth.models import User
from django.db import models, transaction
from django.db.models import QuerySet, Sum
from django.http import HttpRequest
from django.utils.translation import gettext_lazy as _
from compensation.managers import CompensationManager
@ -45,7 +46,7 @@ class AbstractCompensation(BaseObject):
class Meta:
abstract = True
def add_new_deadline(self, form) -> Deadline:
def add_deadline(self, form) -> Deadline:
""" Adds a new deadline to the abstract compensation
Args:
@ -73,7 +74,7 @@ class AbstractCompensation(BaseObject):
self.deadlines.add(deadline)
return deadline
def add_new_action(self, form) -> CompensationAction:
def add_action(self, form) -> CompensationAction:
""" Adds a new action to the compensation
Args:
@ -86,8 +87,6 @@ class AbstractCompensation(BaseObject):
user = form.user
with transaction.atomic():
user_action = UserActionLogEntry.get_created_action(user)
edited_action = UserActionLogEntry.get_edited_action(user, _("Added action"))
comp_action = CompensationAction.objects.create(
action_type=form_data["action_type"],
amount=form_data["amount"],
@ -95,9 +94,6 @@ class AbstractCompensation(BaseObject):
comment=form_data["comment"],
created=user_action,
)
self.modified = edited_action
self.save()
self.log.add(edited_action)
self.actions.add(comp_action)
return comp_action
@ -112,13 +108,7 @@ class AbstractCompensation(BaseObject):
"""
form_data = form.cleaned_data
user = form.user
with transaction.atomic():
user_action = UserActionLogEntry.get_edited_action(user, _("Added state"))
self.log.add(user_action)
self.modified = user_action
self.save()
state = CompensationState.objects.create(
biotope_type=form_data["biotope_type"],
surface=form_data["surface"],
@ -271,6 +261,19 @@ class Compensation(AbstractCompensation, CEFMixin, CoherenceMixin):
)
return docs
def mark_as_edited(self, user: User, request: HttpRequest = None, edit_comment: str = None):
""" Performs internal logic for setting the recordedd/checked state of the related intervention
Args:
user (User): The performing user
request (HttpRequest): The performing request
Returns:
"""
self.intervention.mark_as_edited(user, request, edit_comment)
class CompensationDocument(AbstractDocument):
"""
Specializes document upload for revocations with certain path

View File

@ -7,6 +7,7 @@ Created on: 16.11.21
"""
import shutil
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.core.validators import MinValueValidator
from django.db import models, transaction

View File

@ -274,7 +274,7 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase):
# Now mock the eco account as it would be recorded (with invalid data)
# Make sure the deductible surface is high enough for the request
self.eco_account.toggle_recorded(self.superuser)
self.eco_account.set_recorded(self.superuser)
self.eco_account.refresh_from_db()
self.eco_account.deductable_surface = test_surface + 1.00
self.eco_account.save()

View File

@ -15,7 +15,8 @@ from konova.decorators import *
from konova.forms import RemoveModalForm, SimpleGeomForm
from konova.utils.documents import get_document, remove_document
from konova.utils.generators import generate_qr_code
from konova.utils.message_templates import FORM_INVALID, IDENTIFIER_REPLACED, DATA_UNSHARED_EXPLANATION
from konova.utils.message_templates import FORM_INVALID, IDENTIFIER_REPLACED, DATA_UNSHARED_EXPLANATION, \
CHECKED_RECORDED_RESET
from konova.utils.user_checks import in_group
@ -130,8 +131,15 @@ 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
# 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)
messages.success(request, _("Compensation {} edited").format(comp.identifier))
return redirect("compensation:detail", id=comp.id)
else:
@ -238,7 +246,7 @@ def remove_view(request: HttpRequest, id: str):
"""
comp = get_object_or_404(Compensation, id=id)
form = RemoveModalForm(request.POST or None, instance=comp, user=request.user)
form = RemoveModalForm(request.POST or None, instance=comp, request=request)
return form.process_request(
request=request,
msg_success=_("Compensation removed"),
@ -259,7 +267,7 @@ def new_document_view(request: HttpRequest, id: str):
"""
comp = get_object_or_404(Compensation, id=id)
form = NewCompensationDocumentForm(request.POST or None, request.FILES or None, instance=comp, user=request.user)
form = NewCompensationDocumentForm(request.POST or None, request.FILES or None, instance=comp, request=request)
return form.process_request(
request,
msg_success=_("Document added")
@ -328,7 +336,7 @@ def state_new_view(request: HttpRequest, id: str):
"""
comp = get_object_or_404(Compensation, id=id)
form = NewStateModalForm(request.POST or None, instance=comp, user=request.user)
form = NewStateModalForm(request.POST or None, instance=comp, request=request)
return form.process_request(
request,
msg_success=_("State added")
@ -349,7 +357,7 @@ def action_new_view(request: HttpRequest, id: str):
"""
comp = get_object_or_404(Compensation, id=id)
form = NewActionModalForm(request.POST or None, instance=comp, user=request.user)
form = NewActionModalForm(request.POST or None, instance=comp, request=request)
return form.process_request(
request,
msg_success=_("Action added")
@ -370,7 +378,7 @@ def deadline_new_view(request: HttpRequest, id: str):
"""
comp = get_object_or_404(Compensation, id=id)
form = NewDeadlineModalForm(request.POST or None, instance=comp, user=request.user)
form = NewDeadlineModalForm(request.POST or None, instance=comp, request=request)
return form.process_request(
request,
msg_success=_("Deadline added")
@ -392,7 +400,7 @@ def state_remove_view(request: HttpRequest, id: str, state_id: str):
"""
state = get_object_or_404(CompensationState, id=state_id)
form = RemoveModalForm(request.POST or None, instance=state, user=request.user)
form = RemoveModalForm(request.POST or None, instance=state, request=request)
return form.process_request(
request,
msg_success=_("State removed")
@ -414,7 +422,7 @@ def action_remove_view(request: HttpRequest, id: str, action_id: str):
"""
action = get_object_or_404(CompensationAction, id=action_id)
form = RemoveModalForm(request.POST or None, instance=action, user=request.user)
form = RemoveModalForm(request.POST or None, instance=action, request=request)
return form.process_request(
request,
msg_success=_("Action removed")

View File

@ -250,7 +250,7 @@ def remove_view(request: HttpRequest, id: str):
messages.info(request, CANCEL_ACC_RECORDED_OR_DEDUCTED)
return redirect("compensation:acc-detail", id=id)
form = RemoveModalForm(request.POST or None, instance=acc, user=request.user)
form = RemoveModalForm(request.POST or None, instance=acc, request=request)
return form.process_request(
request=request,
msg_success=_("Eco-account removed"),
@ -278,7 +278,7 @@ def deduction_remove_view(request: HttpRequest, id: str, deduction_id: str):
except ObjectDoesNotExist:
raise Http404("Unknown deduction")
form = RemoveModalForm(request.POST or None, instance=eco_deduction, user=request.user)
form = RemoveModalForm(request.POST or None, instance=eco_deduction, request=request)
return form.process_request(
request=request,
msg_success=_("Deduction removed")
@ -325,7 +325,7 @@ def record_view(request: HttpRequest, id:str):
"""
acc = get_object_or_404(EcoAccount, id=id)
form = RecordModalForm(request.POST or None, instance=acc, user=request.user)
form = RecordModalForm(request.POST or None, instance=acc, request=request)
msg_succ = _("{} unrecorded") if acc.recorded else _("{} recorded")
msg_succ = msg_succ.format(acc.identifier)
return form.process_request(
@ -348,7 +348,7 @@ def state_new_view(request: HttpRequest, id: str):
"""
acc = get_object_or_404(EcoAccount, id=id)
form = NewStateModalForm(request.POST or None, instance=acc, user=request.user)
form = NewStateModalForm(request.POST or None, instance=acc, request=request)
return form.process_request(
request,
msg_success=_("State added")
@ -369,7 +369,7 @@ def action_new_view(request: HttpRequest, id: str):
"""
acc = get_object_or_404(EcoAccount, id=id)
form = NewActionModalForm(request.POST or None, instance=acc, user=request.user)
form = NewActionModalForm(request.POST or None, instance=acc, request=request)
return form.process_request(
request,
msg_success=_("Action added")
@ -391,7 +391,7 @@ def state_remove_view(request: HttpRequest, id: str, state_id: str):
"""
state = get_object_or_404(CompensationState, id=state_id)
form = RemoveModalForm(request.POST or None, instance=state, user=request.user)
form = RemoveModalForm(request.POST or None, instance=state, request=request)
return form.process_request(
request,
msg_success=_("State removed")
@ -413,7 +413,7 @@ def action_remove_view(request: HttpRequest, id: str, action_id: str):
"""
action = get_object_or_404(CompensationAction, id=action_id)
form = RemoveModalForm(request.POST or None, instance=action, user=request.user)
form = RemoveModalForm(request.POST or None, instance=action, request=request)
return form.process_request(
request,
msg_success=_("Action removed")
@ -434,7 +434,7 @@ def deadline_new_view(request: HttpRequest, id: str):
"""
acc = get_object_or_404(EcoAccount, id=id)
form = NewDeadlineModalForm(request.POST or None, instance=acc, user=request.user)
form = NewDeadlineModalForm(request.POST or None, instance=acc, request=request)
return form.process_request(
request,
msg_success=_("Deadline added")
@ -454,7 +454,7 @@ def new_document_view(request: HttpRequest, id: str):
"""
acc = get_object_or_404(EcoAccount, id=id)
form = NewEcoAccountDocumentForm(request.POST or None, request.FILES or None, instance=acc, user=request.user)
form = NewEcoAccountDocumentForm(request.POST or None, request.FILES or None, instance=acc, request=request)
return form.process_request(
request,
msg_success=_("Document added")
@ -524,7 +524,7 @@ def new_deduction_view(request: HttpRequest, id: str):
"""
acc = get_object_or_404(EcoAccount, id=id)
form = NewDeductionModalForm(request.POST or None, instance=acc, user=request.user)
form = NewDeductionModalForm(request.POST or None, instance=acc, request=request)
return form.process_request(
request,
msg_success=_("Deduction added")

View File

@ -30,7 +30,7 @@ def new_payment_view(request: HttpRequest, intervention_id: str):
"""
intervention = get_object_or_404(Intervention, id=intervention_id)
form = NewPaymentForm(request.POST or None, instance=intervention, user=request.user)
form = NewPaymentForm(request.POST or None, instance=intervention, request=request)
return form.process_request(
request,
msg_success=_("Payment added")
@ -50,7 +50,7 @@ def payment_remove_view(request: HttpRequest, id: str):
"""
payment = get_object_or_404(Payment, id=id)
form = RemoveModalForm(request.POST or None, instance=payment, user=request.user)
form = RemoveModalForm(request.POST or None, instance=payment, request=request)
return form.process_request(
request=request,
msg_success=_("Payment removed"),

View File

@ -7,6 +7,7 @@ Created on: 15.11.21
"""
import shutil
from django.contrib.auth.models import User
from django.db import models
from django.db.models import QuerySet

View File

@ -237,7 +237,7 @@ def remove_view(request: HttpRequest, id: str):
"""
ema = get_object_or_404(Ema, id=id)
form = RemoveModalForm(request.POST or None, instance=ema, user=request.user)
form = RemoveModalForm(request.POST or None, instance=ema, request=request)
return form.process_request(
request=request,
msg_success=_("EMA removed"),
@ -260,7 +260,7 @@ def record_view(request: HttpRequest, id: str):
"""
ema = get_object_or_404(Ema, id=id)
msg_succ = _("{} unrecorded") if ema.recorded else _("{} recorded")
form = RecordModalForm(request.POST or None, instance=ema, user=request.user)
form = RecordModalForm(request.POST or None, instance=ema, request=request)
return form.process_request(
request=request,
msg_success=msg_succ.format("EMA"),
@ -281,7 +281,7 @@ def state_new_view(request: HttpRequest, id: str):
"""
ema = get_object_or_404(Ema, id=id)
form = NewStateModalForm(request.POST or None, instance=ema, user=request.user)
form = NewStateModalForm(request.POST or None, instance=ema, request=request)
return form.process_request(
request,
msg_success=_("State added")
@ -302,7 +302,7 @@ def action_new_view(request: HttpRequest, id: str):
"""
ema = get_object_or_404(Ema, id=id)
form = NewActionModalForm(request.POST or None, instance=ema, user=request.user)
form = NewActionModalForm(request.POST or None, instance=ema, request=request)
return form.process_request(
request,
msg_success=_("Action added")
@ -323,7 +323,7 @@ def deadline_new_view(request: HttpRequest, id: str):
"""
ema = get_object_or_404(Ema, id=id)
form = NewDeadlineModalForm(request.POST or None, instance=ema, user=request.user)
form = NewDeadlineModalForm(request.POST or None, instance=ema, request=request)
return form.process_request(
request,
msg_success=_("Deadline added")
@ -343,7 +343,7 @@ def document_new_view(request: HttpRequest, id: str):
"""
ema = get_object_or_404(Ema, id=id)
form = NewEmaDocumentForm(request.POST or None, request.FILES or None, instance=ema, user=request.user)
form = NewEmaDocumentForm(request.POST or None, request.FILES or None, instance=ema, request=request)
return form.process_request(
request,
msg_success=_("Document added")
@ -413,7 +413,7 @@ def state_remove_view(request: HttpRequest, id: str, state_id: str):
"""
state = get_object_or_404(CompensationState, id=state_id)
form = RemoveModalForm(request.POST or None, instance=state, user=request.user)
form = RemoveModalForm(request.POST or None, instance=state, request=request)
return form.process_request(
request,
msg_success=_("State removed")
@ -435,7 +435,7 @@ def action_remove_view(request: HttpRequest, id: str, action_id: str):
"""
action = get_object_or_404(CompensationAction, id=action_id)
form = RemoveModalForm(request.POST or None, instance=action, user=request.user)
form = RemoveModalForm(request.POST or None, instance=action, request=request)
return form.process_request(
request,
msg_success=_("Action removed")
@ -463,7 +463,7 @@ def report_view(request:HttpRequest, id: str):
# Prepare data for map viewer
geom_form = SimpleGeomForm(
instance=ema
instance=ema,
)
qrcode_img = generate_qr_code(
request.build_absolute_uri(reverse("ema:report", args=(id,))),

View File

@ -348,10 +348,7 @@ class EditInterventionForm(NewInterventionForm):
self.instance.save()
# Uncheck and unrecord intervention due to changed data
if self.instance.checked:
self.instance.set_unchecked()
if self.instance.recorded:
self.instance.set_unrecorded(user)
self.instance.mark_as_edited(user)
return self.instance

View File

@ -169,6 +169,7 @@ class NewRevocationModalForm(BaseModalForm):
def save(self):
revocation = self.instance.add_revocation(self)
self.instance.mark_as_edited(self.user, self.request)
return revocation
@ -225,7 +226,7 @@ class CheckModalForm(BaseModalForm):
"""
with transaction.atomic():
self.instance.toggle_checked(self.user)
self.instance.set_checked(self.user)
class NewDeductionModalForm(BaseModalForm):
@ -332,6 +333,7 @@ class NewDeductionModalForm(BaseModalForm):
def save(self):
deduction = self.instance.add_deduction(self)
self.instance.mark_as_edited(self.user, self.request)
return deduction

View File

@ -10,7 +10,7 @@ import shutil
from django.contrib.auth.models import User
from django.db import models, transaction
from django.db.models import QuerySet
from django.utils.translation import gettext_lazy as _
from django.http import HttpRequest
from compensation.models import EcoAccountDeduction
from intervention.managers import InterventionManager
@ -138,40 +138,32 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec
)
return revoc_docs, regular_docs
def toggle_recorded(self, user: User):
""" Toggle the recorded state
def set_unchecked(self):
super().set_unchecked()
For interventions the recorded action needs to be added to their compensation objects as well
def set_checked(self, user: User) -> UserActionLogEntry:
log_entry = super().set_checked(user)
self.add_log_entry_to_compensations(log_entry)
return log_entry
def set_unrecorded(self, user: User):
log_entry = super().set_unrecorded(user)
self.add_log_entry_to_compensations(log_entry)
def set_recorded(self, user: User) -> UserActionLogEntry:
log_entry = super().set_recorded(user)
self.add_log_entry_to_compensations(log_entry)
return log_entry
def add_log_entry_to_compensations(self, log_entry: UserActionLogEntry):
""" Adds the log entry to related compensations
Args:
user (User): The performing user
log_entry (UserActionLogEntry): The log entry
Returns:
"""
log_entry = super().toggle_recorded(user)
# Add this action to the linked compensation logs as well
comps = self.compensations.all()
for comp in comps:
comp.log.add(log_entry)
def toggle_checked(self, user: User) -> UserActionLogEntry:
""" Toggle the checked state
For interventions the checked action needs to be added to their compensation objects as well
Args:
user (User): The performing user
Returns:
"""
log_entry = super().toggle_checked(user)
# Leave if the log_entry is None (means "unchecked")
if log_entry is None:
return
# Add this action to the linked compensation logs as well
comps = self.compensations.all()
for comp in comps:
comp.log.add(log_entry)
@ -190,8 +182,6 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec
user = form.user
with transaction.atomic():
created_action = UserActionLogEntry.get_created_action(user)
edited_action = UserActionLogEntry.get_edited_action(user, _("Added payment"))
pay = Payment.objects.create(
created=created_action,
amount=form_data.get("amount", -1),
@ -199,10 +189,7 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec
comment=form_data.get("comment", None),
intervention=self,
)
self.log.add(edited_action)
self.modified = edited_action
self.save()
return pay
return pay
def add_revocation(self, form):
""" Adds a new revocation to the intervention
@ -217,7 +204,6 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec
user = form.user
with transaction.atomic():
created_action = UserActionLogEntry.get_created_action(user)
edited_action = UserActionLogEntry.get_edited_action(user)
revocation = Revocation.objects.create(
date=form_data["date"],
@ -225,9 +211,6 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec
comment=form_data["comment"],
created=created_action,
)
self.modified = edited_action
self.save()
self.log.add(edited_action)
if form_data["file"]:
RevocationDocument.objects.create(
@ -252,14 +235,7 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec
user = form.user
with transaction.atomic():
# Create log entry
user_action_edit = UserActionLogEntry.get_edited_action(user)
user_action_create = UserActionLogEntry.get_created_action(user)
self.log.add(user_action_edit)
self.modified = user_action_edit
self.save()
deduction = EcoAccountDeduction.objects.create(
intervention=self,
account=form_data["account"],
@ -268,6 +244,25 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec
)
return deduction
def mark_as_edited(self, performing_user: User, request: HttpRequest = None, edit_comment: str = None):
""" In case the object or a related object changed, internal processes need to be started, such as
unrecord and uncheck
Args:
performing_user (User): The user which performed the editing action
Returns:
"""
user_action_edit = UserActionLogEntry.get_edited_action(performing_user, comment=edit_comment)
self.log.add(user_action_edit)
self.modified = user_action_edit
self.save()
super().mark_as_edited(performing_user, request)
if self.checked:
self.set_unchecked()
class InterventionDocument(AbstractDocument):
"""

View File

@ -123,7 +123,7 @@ def new_document_view(request: HttpRequest, id: str):
"""
intervention = get_object_or_404(Intervention, id=id)
form = NewInterventionDocumentForm(request.POST or None, request.FILES or None, instance=intervention, user=request.user)
form = NewInterventionDocumentForm(request.POST or None, request.FILES or None, instance=intervention, request=request)
return form.process_request(
request,
msg_success=_("Document added")
@ -233,7 +233,7 @@ def detail_view(request: HttpRequest, id: str):
is_data_shared = intervention.is_shared_with(user=_user)
geom_form = SimpleGeomForm(
instance=intervention
instance=intervention,
)
# Inform user about revocation
@ -320,7 +320,7 @@ def remove_view(request: HttpRequest, id: str):
"""
obj = Intervention.objects.get(id=id)
identifier = obj.identifier
form = RemoveModalForm(request.POST or None, instance=obj, user=request.user)
form = RemoveModalForm(request.POST or None, instance=obj, request=request)
return form.process_request(
request,
_("{} removed").format(identifier),
@ -341,7 +341,7 @@ def remove_revocation_view(request: HttpRequest, id: str):
"""
obj = Revocation.objects.get(id=id)
form = RemoveModalForm(request.POST or None, instance=obj, user=request.user)
form = RemoveModalForm(request.POST or None, instance=obj, request=request)
return form.process_request(
request,
_("Revocation removed"),
@ -402,7 +402,7 @@ def create_share_view(request: HttpRequest, id: str):
"""
intervention = get_object_or_404(Intervention, id=id)
form = ShareModalForm(request.POST or None, instance=intervention, request=request, user=request.user)
form = ShareModalForm(request.POST or None, instance=intervention, request=request)
return form.process_request(
request,
msg_success=_("Share settings updated")
@ -423,7 +423,7 @@ def check_view(request: HttpRequest, id: str):
"""
intervention = get_object_or_404(Intervention, id=id)
form = CheckModalForm(request.POST or None, instance=intervention, user=request.user)
form = CheckModalForm(request.POST or None, instance=intervention, request=request)
return form.process_request(
request,
msg_success=_("Check performed"),
@ -445,7 +445,7 @@ def new_revocation_view(request: HttpRequest, id: str):
"""
intervention = get_object_or_404(Intervention, id=id)
form = NewRevocationModalForm(request.POST or None, request.FILES or None, instance=intervention, user=request.user)
form = NewRevocationModalForm(request.POST or None, request.FILES or None, instance=intervention, request=request)
return form.process_request(
request,
msg_success=_("Revocation added")
@ -492,7 +492,7 @@ def new_deduction_view(request: HttpRequest, id: str):
"""
intervention = get_object_or_404(Intervention, id=id)
form = NewDeductionModalForm(request.POST or None, instance=intervention, user=request.user)
form = NewDeductionModalForm(request.POST or None, instance=intervention, request=request)
return form.process_request(
request,
msg_success=_("Deduction added")
@ -513,7 +513,7 @@ def record_view(request: HttpRequest, id: str):
"""
intervention = get_object_or_404(Intervention, id=id)
form = RecordModalForm(request.POST or None, instance=intervention, user=request.user)
form = RecordModalForm(request.POST or None, instance=intervention, request=request)
msg_succ = _("{} unrecorded") if intervention.recorded else _("{} recorded")
msg_succ = msg_succ.format(intervention.identifier)
return form.process_request(

View File

@ -14,11 +14,10 @@ from django import forms
from django.contrib import messages
from django.contrib.auth.models import User
from django.contrib.gis.forms import OSMWidget, MultiPolygonField
from django.contrib.gis.geos import Polygon, MultiPolygon
from django.contrib.gis.geos import MultiPolygon
from django.db import transaction
from django.http import HttpRequest, HttpResponseRedirect
from django.shortcuts import render
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from konova.contexts import BaseContext
@ -39,15 +38,16 @@ class BaseForm(forms.Form):
cancel_redirect = None
form_caption = None
instance = None # The data holding model object
request = None
form_attrs = {} # Holds additional attributes, that can be used in the template
has_required_fields = False # Automatically set. Triggers hint rendering in templates
show_cancel_btn = True
def __init__(self, *args, **kwargs):
self.instance = kwargs.pop("instance", None)
self.user = kwargs.pop("user", None)
super().__init__(*args, **kwargs)
if self.request is not None:
self.user = self.request.user
# Check for required fields
for _field_name, _field_val in self.fields.items():
if _field_val.required:
@ -485,5 +485,8 @@ class RecordModalForm(BaseModalForm):
def save(self):
with transaction.atomic():
if self.cleaned_data["confirm"]:
self.instance.toggle_recorded(self.user)
if self.instance.recorded:
self.instance.set_unrecorded(self.user)
else:
self.instance.set_recorded(self.user)
return self.instance

View File

@ -0,0 +1,268 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 16.11.21
"""
from compensation.models import CompensationState, Compensation, EcoAccount, CompensationAction
from ema.models import Ema
from intervention.models import Intervention
from konova.management.commands.setup import BaseKonovaCommand
from konova.models import Deadline, Geometry
from user.models import UserActionLogEntry
class Command(BaseKonovaCommand):
help = "Checks the database' sanity and removes unused entries"
def handle(self, *args, **options):
try:
self.sanitize_log_entries()
self.sanitize_compensation_states()
self.sanitize_actions()
self.sanitize_deadlines()
self.sanitize_geometries()
except KeyboardInterrupt:
self._break_line()
exit(-1)
def get_all_log_entries_ids(self, cls):
""" Getter for all log entry ids of a model
Args:
cls (Intervention|Compensation|Ema|EcoAccount): The model class
Returns:
"""
all_objects = cls.objects.all().prefetch_related(
"log",
)
all_ids = all_objects.exclude(
log__isnull=True,
).values_list(
"log__id",
flat=True,
)
return all_ids
def sanitize_log_entries(self):
""" Removes log entries which are not attached to any logs
Returns:
"""
self._write_warning("=== Sanitize log entries ===")
all_log_entries = UserActionLogEntry.objects.all()
intervention_log_entries_ids = self.get_all_log_entries_ids(Intervention)
attached_log_entries_id = intervention_log_entries_ids.union(
self.get_all_log_entries_ids(Compensation),
self.get_all_log_entries_ids(EcoAccount),
self.get_all_log_entries_ids(Ema),
)
unattached_log_entries = all_log_entries.exclude(id__in=attached_log_entries_id)
num_entries = unattached_log_entries.count()
if num_entries > 0:
self._write_error(f"Found {num_entries} log entries not attached to anything. Delete now...")
unattached_log_entries.delete()
self._write_success("Log entries deleted.")
else:
self._write_success("No unattached log entries found.")
self._break_line()
def get_all_action_ids(self, cls):
""" Getter for all action ids of a model
Args:
cls (Compensation|Ema|EcoAccount): The model class
Returns:
"""
all_objects = cls.objects.all().prefetch_related(
"actions",
)
all_ids = all_objects.exclude(
actions__isnull=True,
).values_list(
"actions__id",
flat=True,
)
return all_ids
def sanitize_actions(self):
""" Removes actions which are not attached to any entries
Returns:
"""
self._write_warning("=== Sanitize compensation actions ===")
all_actions = CompensationAction.objects.all()
compensation_action_ids = self.get_all_action_ids(Compensation)
attached_action_ids = compensation_action_ids.union(
self.get_all_action_ids(EcoAccount),
self.get_all_action_ids(Ema),
)
unattached_actions = all_actions.exclude(id__in=attached_action_ids)
num_entries = unattached_actions.count()
if num_entries > 0:
self._write_error(f"Found {num_entries} actions not attached to anything. Delete now...")
unattached_actions.delete()
self._write_success("Actions deleted.")
else:
self._write_success("No unattached actions found.")
self._break_line()
def get_all_deadline_ids(self, cls):
""" Getter for all deadline ids of a model
Args:
cls (Compensation|Ema|EcoAccount): The model class
Returns:
"""
all_objects = cls.objects.all().prefetch_related(
"deadlines",
)
all_ids = all_objects.exclude(
deadlines__isnull=True,
).values_list(
"deadlines__id",
flat=True,
)
return all_ids
def sanitize_deadlines(self):
""" Removes deadlines which are not attached to any entries
Returns:
"""
self._write_warning("=== Sanitize deadlines ===")
all_deadlines = Deadline.objects.all()
compensation_deadline_ids = self.get_all_deadline_ids(Compensation)
attached_deadline_ids = compensation_deadline_ids.union(
self.get_all_deadline_ids(EcoAccount),
self.get_all_deadline_ids(Ema),
)
unattached_deadlines = all_deadlines.exclude(id__in=attached_deadline_ids)
num_entries = unattached_deadlines.count()
if num_entries > 0:
self._write_error(f"Found {num_entries} deadlines not attached to anything. Delete now...")
unattached_deadlines.delete()
self._write_success("Deadlines deleted.")
else:
self._write_success("No unattached deadlines found.")
self._break_line()
def get_all_geometry_ids(self, cls):
""" Getter for all geometry ids of a model
Args:
cls (Intervention|Compensation|Ema|EcoAccount): The model class
Returns:
"""
all_objects = cls.objects.all().prefetch_related(
"geometry",
)
all_ids = all_objects.exclude(
geometry__isnull=True,
).values_list(
"geometry__id",
flat=True,
)
return all_ids
def sanitize_geometries(self):
""" Removes geometries which are not attached to any entries
Returns:
"""
self._write_warning("=== Sanitize geometries ===")
all_geometries = Geometry.objects.all()
compensation_geometry_ids = self.get_all_geometry_ids(Compensation)
attached_geometry_ids = compensation_geometry_ids.union(
self.get_all_geometry_ids(Intervention),
self.get_all_geometry_ids(EcoAccount),
self.get_all_geometry_ids(Ema),
)
unattached_geometries = all_geometries.exclude(id__in=attached_geometry_ids)
num_entries = unattached_geometries.count()
if num_entries > 0:
self._write_error(f"Found {num_entries} geometries not attached to anything. Delete now...")
unattached_geometries.delete()
self._write_success("Deadlines deleted.")
else:
self._write_success("No unattached geometries found.")
self._break_line()
def get_all_state_ids(self, cls):
""" Getter for all states (before and after) of a class
Args:
cls ():
Returns:
"""
all_objects = cls.objects.all().prefetch_related(
"before_states",
"after_states",
)
before_state_ids = all_objects.exclude(
before_states__isnull=True,
).values_list(
"before_states__id",
flat=True,
)
after_state_ids = all_objects.exclude(
after_states__isnull=True,
).values_list(
"after_states__id",
flat=True,
)
all_ids = after_state_ids.union(before_state_ids)
return all_ids
def sanitize_compensation_states(self):
""" Removes unattached compensation states
Returns:
"""
self._write_warning("=== Sanitize compensation states ===")
all_states = CompensationState.objects.all()
compensation_state_ids = self.get_all_state_ids(Compensation)
account_state_ids = self.get_all_state_ids(EcoAccount)
ema_state_ids = self.get_all_state_ids(Ema)
attached_state_ids = compensation_state_ids.union(account_state_ids, ema_state_ids)
unattached_states = all_states.exclude(
id__in=attached_state_ids
)
num_unattached_states = unattached_states.count()
if num_unattached_states > 0:
self._write_error(f"Found {num_unattached_states} unused compensation states. Delete now...")
unattached_states.delete()
self._write_success("Unused states deleted.")
else:
self._write_success("No unused states found.")
self._break_line()

View File

@ -8,9 +8,10 @@ Created on: 15.11.21
import uuid
from django.contrib import messages
from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist
from django.utils import timezone
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, \
@ -19,6 +20,7 @@ from ema.settings import EMA_ACCOUNT_IDENTIFIER_LENGTH, EMA_ACCOUNT_IDENTIFIER_T
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
from user.models import UserActionLogEntry, UserAction
@ -203,6 +205,8 @@ class RecordableObjectMixin(models.Model):
Returns:
"""
if not self.recorded:
return None
action = UserActionLogEntry.get_unrecorded_action(user)
self.recorded = None
self.save()
@ -218,26 +222,36 @@ class RecordableObjectMixin(models.Model):
Returns:
"""
if self.recorded:
return None
action = UserActionLogEntry.get_recorded_action(user)
self.recorded = action
self.save()
self.log.add(action)
return action
def toggle_recorded(self, user: User) -> UserActionLogEntry:
""" Un/Record intervention
def mark_as_edited(self, performing_user: User, request: HttpRequest = None, edit_comment: str = None):
""" In case the object or a related object changed, internal processes need to be started, such as
unrecord and uncheck
Args:
user (User): Performing user
performing_user (User): The user which performed the editing action
Returns:
"""
if not self.recorded:
ret_log_entry = self.set_recorded(user)
else:
ret_log_entry = self.set_unrecorded(user)
return ret_log_entry
action = UserActionLogEntry.get_edited_action(performing_user, edit_comment)
self.modified = action
self.log.add(action)
self.save()
if self.recorded:
self.set_unrecorded(performing_user)
if request:
messages.info(
request,
CHECKED_RECORDED_RESET
)
class CheckableObjectMixin(models.Model):
@ -262,6 +276,9 @@ class CheckableObjectMixin(models.Model):
Returns:
"""
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
# log history, which is not what we want!
self.checked = None
@ -277,27 +294,15 @@ class CheckableObjectMixin(models.Model):
Returns:
"""
if self.checked:
# Nothing to do
return
action = UserActionLogEntry.get_checked_action(user)
self.checked = action
self.save()
self.log.add(action)
return action
def toggle_checked(self, user: User) -> UserActionLogEntry:
""" Un/Record intervention
Args:
user (User): Performing user
Returns:
"""
if not self.checked:
ret_log_entry = self.set_checked(user)
else:
ret_log_entry = self.set_unchecked()
return ret_log_entry
class ShareableObjectMixin(models.Model):
# Users having access on this object

View File

@ -46,7 +46,7 @@ def remove_document(request: HttpRequest, doc: AbstractDocument):
"""
title = doc.title
form = RemoveModalForm(request.POST or None, instance=doc, user=request.user)
form = RemoveModalForm(request.POST or None, instance=doc, request=request)
return form.process_request(
request=request,
msg_success=_("Document '{}' deleted").format(title)

View File

@ -19,4 +19,10 @@ MISSING_GROUP_PERMISSION = _("You need to be part of another user group.")
CHECKED_RECORDED_RESET = _("Status of Checked and Recorded reseted")
# ECO ACCOUNT
CANCEL_ACC_RECORDED_OR_DEDUCTED = _("Action canceled. Eco account is recorded or deductions exist. Only conservation office member can perform this action.")
CANCEL_ACC_RECORDED_OR_DEDUCTED = _("Action canceled. Eco account is recorded or deductions exist. Only conservation office member can perform this action.")
# Edited
EDITED_GENERAL_DATA = _("Edited general data")
ADDED_COMPENSATION_STATE = _("Added compensation state")
ADDED_DEADLINE = _("Added deadline")
ADDED_COMPENSATION_ACTION = _("Added compensation action")

View File

@ -109,7 +109,7 @@ def remove_deadline_view(request: HttpRequest, id:str):
"""
deadline = get_object_or_404(Deadline, id=id)
form = RemoveModalForm(request.POST or None, instance=deadline, user=request.user)
form = RemoveModalForm(request.POST or None, instance=deadline, request=request)
return form.process_request(
request,
msg_success=_("Deadline removed")

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -83,7 +83,7 @@ def contact_view(request: HttpRequest, id: str):
"""
user = get_object_or_404(User, id=id)
form = UserContactForm(request.POST or None, instance=user, user=request.user)
form = UserContactForm(request.POST or None, instance=user, request=request)
template = "modal/modal_form.html"
context = {
"form": form,