Created|Deleted refactoring

* refactors base attributes created and deleted into UserActionLogEntry foreign keys
* refactors all related queries and process logic
* fixes binding_on into binding_date in intervention/detail/view.html
* adds basic __str__ for some models
*
This commit is contained in:
mipel 2021-08-02 11:52:20 +02:00
parent 92cacd7aaa
commit f113bad733
17 changed files with 138 additions and 60 deletions

View File

@ -37,8 +37,7 @@ class CompensationAdmin(admin.ModelAdmin):
"id", "id",
"identifier", "identifier",
"title", "title",
"created_on", "created",
"created_by",
] ]
@ -47,8 +46,6 @@ class EcoAccountAdmin(admin.ModelAdmin):
"id", "id",
"identifier", "identifier",
"title", "title",
"created_on",
"created_by",
] ]
@ -57,8 +54,6 @@ class PaymentAdmin(admin.ModelAdmin):
"id", "id",
"amount", "amount",
"due_on", "due_on",
"created_by",
"created_on",
] ]
@ -68,8 +63,6 @@ class EcoAccountWithdrawAdmin(admin.ModelAdmin):
"account", "account",
"intervention", "intervention",
"amount", "amount",
"created_by",
"created_on",
] ]

View File

@ -10,7 +10,9 @@ from django.db import transaction
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from compensation.models import Payment from compensation.models import Payment
from konova.enums import UserActionLogEntryEnum
from konova.forms import BaseForm, BaseModalForm from konova.forms import BaseForm, BaseModalForm
from user.models import UserActionLogEntry
class NewCompensationForm(BaseForm): class NewCompensationForm(BaseForm):
@ -56,8 +58,12 @@ class NewPaymentForm(BaseModalForm):
def save(self): def save(self):
with transaction.atomic(): with transaction.atomic():
action = UserActionLogEntry.objects.create(
user=self.user,
action=UserActionLogEntryEnum.CREATED.value,
)
pay = Payment.objects.create( pay = Payment.objects.create(
created_by=self.user, created=action,
amount=self.cleaned_data.get("amount", -1), amount=self.cleaned_data.get("amount", -1),
due_on=self.cleaned_data.get("due", None), due_on=self.cleaned_data.get("due", None),
comment=self.cleaned_data.get("transfer_note", None), comment=self.cleaned_data.get("transfer_note", None),

View File

@ -8,14 +8,17 @@ Created on: 17.11.20
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.gis.db import models from django.contrib.gis.db import models
from django.core.validators import MinValueValidator, MaxValueValidator from django.core.validators import MinValueValidator, MaxValueValidator
from django.db import transaction
from django.utils import timezone from django.utils import timezone
from django.utils.timezone import now from django.utils.timezone import now
from compensation.settings import COMPENSATION_IDENTIFIER_LENGTH, COMPENSATION_IDENTIFIER_TEMPLATE from compensation.settings import COMPENSATION_IDENTIFIER_LENGTH, COMPENSATION_IDENTIFIER_TEMPLATE
from intervention.models import Intervention, ResponsibilityData from intervention.models import Intervention, ResponsibilityData
from konova.enums import UserActionLogEntryEnum
from konova.models import BaseObject, BaseResource, Geometry, UuidModel from konova.models import BaseObject, BaseResource, Geometry, UuidModel
from konova.utils.generators import generate_random_string from konova.utils.generators import generate_random_string
from organisation.models import Organisation from organisation.models import Organisation
from user.models import UserActionLogEntry
class Payment(BaseResource): class Payment(BaseResource):
@ -68,10 +71,11 @@ class CompensationAction(BaseResource):
control = models.ForeignKey(CompensationControl, on_delete=models.SET_NULL, null=True, blank=True) control = models.ForeignKey(CompensationControl, on_delete=models.SET_NULL, null=True, blank=True)
class Compensation(BaseObject): class AbstractCompensation(BaseObject):
""" """
The compensation holds information about which actions have to be performed until which date, who is in charge Abstract compensation model which holds basic attributes, shared by subclasses like the regular Compensation
of this, which legal authority is the point of contact, and so on. or EcoAccount.
""" """
responsible = models.OneToOneField( responsible = models.OneToOneField(
ResponsibilityData, ResponsibilityData,
@ -93,6 +97,14 @@ class Compensation(BaseObject):
# Holds which intervention is simply a newer version of this dataset # Holds which intervention is simply a newer version of this dataset
next_version = models.ForeignKey("Compensation", null=True, blank=True, on_delete=models.DO_NOTHING) next_version = models.ForeignKey("Compensation", null=True, blank=True, on_delete=models.DO_NOTHING)
class Meta:
abstract = True
class Compensation(AbstractCompensation):
"""
Regular compensation, linked to an intervention
"""
intervention = models.ForeignKey( intervention = models.ForeignKey(
Intervention, Intervention,
on_delete=models.CASCADE, on_delete=models.CASCADE,
@ -101,6 +113,9 @@ class Compensation(BaseObject):
related_name='compensations' related_name='compensations'
) )
def __str__(self):
return "{}".format(self.identifier)
@staticmethod @staticmethod
def _generate_new_identifier() -> str: def _generate_new_identifier() -> str:
""" Generates a new identifier for the intervention object """ Generates a new identifier for the intervention object
@ -131,8 +146,15 @@ class Compensation(BaseObject):
""" """
_now = timezone.now() _now = timezone.now()
_user = kwargs.get("user", None) _user = kwargs.get("user", None)
self.deleted_on = _now with transaction.atomic():
self.deleted_by = _user action = UserActionLogEntry.objects.create(
user=_user,
timestamp=_now,
action=UserActionLogEntryEnum.DELETED.value
)
self.deleted = action
#self.deleted_on = _now
#self.deleted_by = _user
self.save() self.save()
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
@ -145,13 +167,17 @@ class Compensation(BaseObject):
super().save(*args, **kwargs) super().save(*args, **kwargs)
class EcoAccount(Compensation): class EcoAccount(AbstractCompensation):
""" """
An eco account is a kind of 'prepaid' compensation. It can be compared to an account that already has been filled An eco account is a kind of 'prepaid' compensation. It can be compared to an account that already has been filled
with some kind of currency. From this account one is able to 'withdraw' currency for current projects. with some kind of currency. From this account one is able to 'withdraw' currency for current projects.
""" """
# Users having access on this object # Users having access on this object
users = models.ManyToManyField(User) # Not needed in regular Compensation since their access is defined by the linked intervention's access
users = models.ManyToManyField(
User,
help_text="Users having access"
)
def __str__(self): def __str__(self):
return "{}".format(self.identifier) return "{}".format(self.identifier)

View File

@ -51,7 +51,7 @@ class CompensationTable(BaseTable):
lm = tables.Column( lm = tables.Column(
verbose_name=_("Last edit"), verbose_name=_("Last edit"),
orderable=True, orderable=True,
accessor="created_on", accessor="created__timestamp",
) )
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
@ -173,7 +173,7 @@ class EcoAccountTable(BaseTable):
d = tables.Column( d = tables.Column(
verbose_name=_("Created on"), verbose_name=_("Created on"),
orderable=True, orderable=True,
accessor="created_on", accessor="created__timestamp",
) )
ac = tables.Column( ac = tables.Column(
verbose_name=_("Actions"), verbose_name=_("Actions"),

View File

@ -28,7 +28,7 @@ def index_view(request: HttpRequest):
template = "generic_index.html" template = "generic_index.html"
user = request.user user = request.user
compensations = Compensation.objects.filter( compensations = Compensation.objects.filter(
deleted_on=None, deleted=None,
) )
table = CompensationTable( table = CompensationTable(
request=request, request=request,
@ -92,7 +92,7 @@ def account_index_view(request: HttpRequest):
template = "generic_index.html" template = "generic_index.html"
user = request.user user = request.user
eco_accounts = EcoAccount.objects.filter( eco_accounts = EcoAccount.objects.filter(
deleted_on=None, deleted=None,
) )
table = EcoAccountTable( table = EcoAccountTable(
request=request, request=request,

View File

@ -7,8 +7,8 @@ class InterventionAdmin(admin.ModelAdmin):
list_display = [ list_display = [
"id", "id",
"title", "title",
"created_on", "created",
"deleted_on", "deleted",
] ]

View File

@ -15,10 +15,12 @@ from django.urls import reverse
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from intervention.models import Intervention from intervention.models import Intervention
from konova.enums import UserActionLogEntryEnum
from konova.forms import BaseForm, BaseModalForm from konova.forms import BaseForm, BaseModalForm
from konova.models import Document from konova.models import Document
from konova.settings import DEFAULT_LAT, DEFAULT_LON, DEFAULT_ZOOM from konova.settings import DEFAULT_LAT, DEFAULT_LON, DEFAULT_ZOOM
from organisation.models import Organisation from organisation.models import Organisation
from user.models import UserActionLogEntry
class NewInterventionForm(BaseForm): class NewInterventionForm(BaseForm):
@ -115,6 +117,10 @@ class NewInterventionForm(BaseForm):
geometry = self.cleaned_data.get("geometry", Polygon()) geometry = self.cleaned_data.get("geometry", Polygon())
documents = self.cleaned_data.get("documents", []) or [] documents = self.cleaned_data.get("documents", []) or []
action = UserActionLogEntry.objects.create(
user=user,
action=UserActionLogEntryEnum.CREATED.value,
)
intervention = Intervention( intervention = Intervention(
identifier=identifier, identifier=identifier,
title=title, title=title,
@ -124,7 +130,7 @@ class NewInterventionForm(BaseForm):
data_provider=data_provider, data_provider=data_provider,
data_provider_detail=data_provider_detail, data_provider_detail=data_provider_detail,
geometry=geometry, geometry=geometry,
created_by=user, created=action,
) )
intervention.save() intervention.save()
for doc in documents: for doc in documents:

View File

@ -12,6 +12,7 @@ from django.utils import timezone
from django.utils.timezone import now from django.utils.timezone import now
from intervention.settings import INTERVENTION_IDENTIFIER_LENGTH, INTERVENTION_IDENTIFIER_TEMPLATE from intervention.settings import INTERVENTION_IDENTIFIER_LENGTH, INTERVENTION_IDENTIFIER_TEMPLATE
from konova.enums import UserActionLogEntryEnum
from konova.models import BaseObject, Geometry, UuidModel from konova.models import BaseObject, Geometry, UuidModel
from konova.utils import generators from konova.utils import generators
from konova.utils.generators import generate_random_string from konova.utils.generators import generate_random_string
@ -30,6 +31,13 @@ class ResponsibilityData(UuidModel):
conservation_file_number = models.CharField(max_length=1000, blank=True, null=True) conservation_file_number = models.CharField(max_length=1000, blank=True, null=True)
handler = models.CharField(max_length=500, null=True, blank=True, help_text="Refers to 'Eingriffsverursacher'") handler = models.CharField(max_length=500, null=True, blank=True, help_text="Refers to 'Eingriffsverursacher'")
def __str__(self):
return "ZB: {} | ETS: {} | Handler: {}".format(
self.registration_office,
self.conservation_office,
self.handler
)
class LegalData(UuidModel): class LegalData(UuidModel):
""" """
@ -44,6 +52,13 @@ class LegalData(UuidModel):
process_type = models.CharField(max_length=500, null=True, blank=True) process_type = models.CharField(max_length=500, null=True, blank=True)
law = models.CharField(max_length=500, null=True, blank=True) law = models.CharField(max_length=500, null=True, blank=True)
def __str__(self):
return "{} | {} | {}".format(
self.process_type,
self.law,
self.id
)
class Intervention(BaseObject): class Intervention(BaseObject):
""" """
@ -121,13 +136,20 @@ class Intervention(BaseObject):
with transaction.atomic(): with transaction.atomic():
# "Delete" related compensations as well # "Delete" related compensations as well
coms = self.compensations.all() coms = self.compensations.all()
action = UserActionLogEntry.objects.create(
user=_user,
timestamp=_now,
action=UserActionLogEntryEnum.DELETED.value
)
for com in coms: for com in coms:
com.deleted_on = _now com.deleted = action
com.deleted_by = _user #com.deleted_on = _now
#com.deleted_by = _user
com.save() com.save()
self.deleted_on = _now self.deleted = action
self.deleted_by = _user #self.deleted_on = _now
#self.deleted_by = _user
self.save() self.save()
@staticmethod @staticmethod

View File

@ -50,7 +50,7 @@ class InterventionTable(BaseTable):
lm = tables.Column( lm = tables.Column(
verbose_name=_("Last edit"), verbose_name=_("Last edit"),
orderable=True, orderable=True,
accessor="created_on", accessor="created__timestamp",
) )
""" """
# ToDo: Decide to keep actions column or to dismiss them # ToDo: Decide to keep actions column or to dismiss them

View File

@ -122,15 +122,15 @@
</tr> </tr>
<tr> <tr>
<th scope="row">{% trans 'Binding on' %}</th> <th scope="row">{% trans 'Binding on' %}</th>
<td class="align-middle">{{intervention.legal.binding_on|default_if_none:""}}</td> <td class="align-middle">{{intervention.legal.binding_date|default_if_none:""}}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">{% trans 'Last modified' %}</th> <th scope="row">{% trans 'Last modified' %}</th>
<td class="align-middle"> <td class="align-middle">
{{intervention.created_on|default_if_none:""|naturalday}} {{intervention.created.timestamp|default_if_none:""|naturalday}}
<br> <br>
{% trans 'by' %} {% trans 'by' %}
{{intervention.created_by|default_if_none:""}} {{intervention.created.user|default_if_none:""}}
</td> </td>
</tr> </tr>
</table> </table>

View File

@ -28,10 +28,10 @@ def index_view(request: HttpRequest):
# Filtering by user access is performed in table filter inside of InterventionTableFilter class # Filtering by user access is performed in table filter inside of InterventionTableFilter class
interventions = Intervention.objects.filter( interventions = Intervention.objects.filter(
deleted_on=None, # not deleted deleted=None, # not deleted
next_version=None, # only newest versions next_version=None, # only newest versions
).order_by( ).order_by(
"-created_on" "-created__timestamp"
) )
table = InterventionTable( table = InterventionTable(
request=request, request=request,
@ -128,8 +128,7 @@ def open_view(request: HttpRequest, id: str):
# Fetch data, filter out deleted related data # Fetch data, filter out deleted related data
intervention = get_object_or_404(Intervention, id=id) intervention = get_object_or_404(Intervention, id=id)
compensations = intervention.compensations.filter( compensations = intervention.compensations.filter(
deleted_on=None, deleted=None,
deleted_by=None,
) )
has_access = intervention.has_access(user=request.user) has_access = intervention.has_access(user=request.user)

View File

@ -13,8 +13,7 @@ from konova.models import Geometry, Document, Deadline
class GeometryAdmin(admin.ModelAdmin): class GeometryAdmin(admin.ModelAdmin):
list_display = [ list_display = [
"id", "id",
"created_on", "created",
"created_by",
] ]
@ -23,8 +22,7 @@ class DocumentAdmin(admin.ModelAdmin):
"id", "id",
"title", "title",
"comment", "comment",
"created_on", "created",
"created_by",
] ]

View File

@ -55,6 +55,8 @@ class UserActionLogEntryEnum(BaseEnum):
""" """
CHECKED = "Checked" CHECKED = "Checked"
RECORDED = "Recorded" RECORDED = "Recorded"
CREATED = "Created"
DELETED = "Deleted"
class DeadlineTypeEnum(BaseEnum): class DeadlineTypeEnum(BaseEnum):

View File

@ -21,8 +21,10 @@ from django.utils import timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from konova.contexts import BaseContext from konova.contexts import BaseContext
from konova.enums import UserActionLogEntryEnum
from konova.models import Document from konova.models import Document
from konova.utils.message_templates import FORM_INVALID from konova.utils.message_templates import FORM_INVALID
from user.models import UserActionLogEntry
class BaseForm(forms.Form): class BaseForm(forms.Form):
@ -101,10 +103,25 @@ class RemoveForm(BaseForm):
return self.cleaned_data.get("check", False) return self.cleaned_data.get("check", False)
def save(self, user: User): def save(self, user: User):
""" Perform generic removing by running the form typical 'save()' method
Args:
user (User): The performing user
Returns:
"""
if self.object_to_remove is not None and self.is_checked(): if self.object_to_remove is not None and self.is_checked():
with transaction.atomic():
self.object_to_remove.is_active = False self.object_to_remove.is_active = False
self.object_to_remove.deleted_on = timezone.now() action = UserActionLogEntry.objects.create(
self.object_to_remove.deleted_by = user user=user,
timestamp=timezone.now(),
action=UserActionLogEntryEnum.DELETED.value
)
self.object_to_remove.deleted = action
#self.object_to_remove.deleted_on = timezone.now()
#self.object_to_remove.deleted_by = user
self.object_to_remove.save() self.object_to_remove.save()
return self.object_to_remove return self.object_to_remove
@ -169,9 +186,15 @@ class RemoveModalForm(BaseModalForm):
self.form_caption = _("Are you sure?") self.form_caption = _("Are you sure?")
def save(self): def save(self):
if hasattr(self.instance, "deleted_on"): if hasattr(self.instance, "deleted"):
self.instance.deleted_on = timezone.now() action = UserActionLogEntry.objects.create(
self.instance.deleted_by = self.user user=self.user,
timestamp=timezone.now(),
action=UserActionLogEntryEnum.DELETED.value,
)
self.instance.deleted = action
#self.instance.deleted_on = timezone.now()
#self.instance.deleted_by = self.user
self.instance.save() self.instance.save()
else: else:
# If the class does not provide restorable delete functionality, we must delete the entry finally # If the class does not provide restorable delete functionality, we must delete the entry finally
@ -270,8 +293,12 @@ class NewDocumentForm(BaseModalForm):
def save(self): def save(self):
with transaction.atomic(): with transaction.atomic():
action = UserActionLogEntry.objects.create(
user=self.user,
action=UserActionLogEntryEnum.CREATED.value,
)
doc = Document.objects.create( doc = Document.objects.create(
created_by=self.user, created=action,
title=self.cleaned_data["title"], title=self.cleaned_data["title"],
comment=self.cleaned_data["comment"], comment=self.cleaned_data["comment"],
document=self.cleaned_data["file"], document=self.cleaned_data["file"],

View File

@ -14,6 +14,7 @@ from django.db import models
from konova.enums import DeadlineTypeEnum from konova.enums import DeadlineTypeEnum
from konova.settings import DEFAULT_SRID from konova.settings import DEFAULT_SRID
from user.models import UserActionLogEntry
class UuidModel(models.Model): class UuidModel(models.Model):
@ -33,8 +34,9 @@ class BaseResource(UuidModel):
""" """
A basic resource model, which defines attributes for every derived model A basic resource model, which defines attributes for every derived model
""" """
created_on = models.DateTimeField(auto_now_add=True, null=True) created = models.ForeignKey(UserActionLogEntry, on_delete=models.SET_NULL, null=True, blank=True, related_name='+')
created_by = models.ForeignKey(User, null=True, on_delete=models.SET_NULL, related_name="+") #created_on = models.DateTimeField(auto_now_add=True, null=True)
#created_by = models.ForeignKey(User, null=True, on_delete=models.SET_NULL, related_name="+")
class Meta: class Meta:
abstract = True abstract = True
@ -48,8 +50,9 @@ class BaseObject(BaseResource):
""" """
identifier = models.CharField(max_length=1000, null=True, blank=True) identifier = models.CharField(max_length=1000, null=True, blank=True)
title = models.CharField(max_length=1000, null=True, blank=True) title = models.CharField(max_length=1000, null=True, blank=True)
deleted_on = models.DateTimeField(null=True, blank=True) deleted = models.ForeignKey(UserActionLogEntry, on_delete=models.SET_NULL, null=True, blank=True, related_name='+')
deleted_by = models.ForeignKey(User, null=True, blank=True, on_delete=models.SET_NULL, related_name="+") #deleted_on = models.DateTimeField(null=True, blank=True)
#deleted_by = models.ForeignKey(User, null=True, blank=True, on_delete=models.SET_NULL, related_name="+")
comment = models.TextField(null=True, blank=True) comment = models.TextField(null=True, blank=True)
class Meta: class Meta:

View File

@ -62,8 +62,7 @@ def home_view(request: HttpRequest):
# First fetch all valid objects (undeleted, only newest versions) # First fetch all valid objects (undeleted, only newest versions)
interventions = Intervention.objects.filter( interventions = Intervention.objects.filter(
deleted_on=None, deleted=None,
deleted_by=None,
next_version=None, next_version=None,
) )
# Then fetch only user related ones # Then fetch only user related ones
@ -73,16 +72,14 @@ def home_view(request: HttpRequest):
# Repeat for other objects # Repeat for other objects
comps = Compensation.objects.filter( comps = Compensation.objects.filter(
deleted_on=None, deleted=None,
deleted_by=None,
next_version=None, next_version=None,
) )
user_comps = comps.filter( user_comps = comps.filter(
intervention__users__in=[user] intervention__users__in=[user]
) )
eco_accs = EcoAccount.objects.filter( eco_accs = EcoAccount.objects.filter(
deleted_on=None, deleted=None,
deleted_by=None,
next_version=None, next_version=None,
) )
user_ecco_accs = eco_accs.filter( user_ecco_accs = eco_accs.filter(

View File

@ -6,8 +6,7 @@ from organisation.models import Organisation
class OrganisationAdmin(admin.ModelAdmin): class OrganisationAdmin(admin.ModelAdmin):
list_display = [ list_display = [
"name", "name",
"created_on", "created",
"created_by",
] ]