# Imports
from django.conf import settings
from django.db import models
from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _
from superdjango.shortcuts import get_user_name
from ..audit.utils import is_audit_model
# Exports
__all__ = (
"CompletedByModel",
)
# Constants
AUTH_USER_MODEL = settings.AUTH_USER_MODEL
# Models
[docs]class CompletedByModel(models.Model):
"""Tracks the user that marked a record as complete as well as the date and time the record was marked complete.
*Completed* may mean different things in different contexts. It just depends on your app.
**Fields**
This class will add the following fields:
- ``cached_completed_by_name``: Since the user is nullable, this captures the name of the user.
- ``completed_by``: The user that completed the record.
- ``completed_dt``: The date and time the record was completed.
- ``is_complete``: A boolean flag.
**Methods**
Conveniences methods have also been added for working with records.
- ``mark_complete()``
- ``mark_incomplete()``
- ``toggle_complete()``
**Implementation**
.. code-block:: py
class Task(CompletedByModel):
# ...
class Meta:
ordering = ["completed_dt"]
"""
STAGE_FIELD_NAME = None
STAGE_FIELD_VALUE = None
STAGE_FIELD_VALUE_INCOMPLETE = None
cached_completed_by_name = models.CharField(
_("completed by"),
blank=True,
help_text=_("the name of the person that marked the record complete."),
max_length=256,
null=True
)
completed_by = models.ForeignKey(
AUTH_USER_MODEL,
blank=True,
help_text=_("Person that marked this complete."),
null=True,
on_delete=models.SET_NULL,
related_name="%(app_label)s_%(class)s_completed_records",
verbose_name=_("completed by")
)
completed_dt = models.DateTimeField(
_("completed date/time"),
blank=True,
help_text=_("Date and time of completion."),
null=True
)
is_complete = models.BooleanField(
_("complete"),
default=False,
help_text=_("Indicates this is complete.")
)
class Meta:
abstract = True
@property
def completed_by_name(self):
"""Alias for ``cached_completed_by_name``."""
return self.cached_completed_by_name
[docs] def mark_complete(self, user, commit=True):
"""Mark the record as complete.
:param user: The user marking the record as complete.
:type user: AUTH_USER_MODEL
:param commit: Save the record after marking it as complete.
:type commit: bool
.. note::
Mark complete changes the record only if ``completed_dt`` is ``None``. This prevents updating the user
or date/time if ``mark_complete()`` is called more than once.
"""
# Only set field values if completed_dt is None.
if self.completed_dt is None:
self.is_complete = True
self.cached_completed_by_name = get_user_name(user)
self.completed_by = user
self.completed_dt = now()
if all([self.STAGE_FIELD_NAME is not None, self.STAGE_FIELD_VALUE is not None]):
setattr(self, self.STAGE_FIELD_NAME, self.STAGE_FIELD_VALUE)
if is_audit_model(self):
# noinspection PyUnresolvedReferences
self.audit(user, commit=False)
if commit:
self.save()
[docs] def mark_incomplete(self, user, commit=True):
"""Mark the record as incomplete.
:param user: The user marking the record as complete.
:type user: AUTH_USER_MODEL
:param commit: Save the record after marking it as incomplete.
:type commit: bool
"""
self.is_complete = False
self.cached_completed_by_name = None
self.completed_by = None
self.completed_dt = None
if all([self.STAGE_FIELD_NAME is not None, self.STAGE_FIELD_VALUE_INCOMPLETE is not None]):
setattr(self, self.STAGE_FIELD_NAME, self.STAGE_FIELD_VALUE_INCOMPLETE)
if is_audit_model(self):
# noinspection PyUnresolvedReferences
self.audit(user, commit=False)
if commit:
self.save()
[docs] def toggle_complete(self, user, commit=True):
"""Toggle the record's completion, automatically setting it to whatever it currently is not.
:param user: The user toggling the record.
:type user: AUTH_USER_MODEL
:param commit: Save the record after marking it as incomplete.
:type commit: bool
"""
if self.is_complete:
self.mark_incomplete(user, commit=commit)
else:
self.completed_dt = None
self.mark_complete(user, commit=commit)