Source code for superdjango.db.timed.models

# Imports

from datetime import timedelta
from django.db import models
from django.utils.translation import gettext_lazy as _
from django.utils.timezone import now
from ..audit.utils import is_audit_model

# Exports

__all__ = (
    "TimedModel",
)

# Constants

SECONDS_PER_DAY = 24 * 60 * 60

# Models


[docs]class TimedModel(models.Model): """Provides date/time fields for noting a starting time and ending time, as well as convenience methods for calculating elapsed time. .. code-block:: python from superdjango.db.timed.models import TimedModel class Meeting(TimedModel): end_date = models.DateField() end_time = models.TimeField() is_all_day = models.BooleanField() start_date = models.DateField() start_time = models.TimeField() """ timer_elapsed_seconds = models.PositiveIntegerField( _("elapsed seconds"), blank=True, help_text=_("Total seconds elapsed from start to end."), null=True ) timer_is_running = models.BooleanField( _("running"), default=False, help_text=_("Indicates whether the timer is running.") ) timer_start_dt = models.DateTimeField( _("start date/time"), blank=True, help_text=_("Date and time the timer was started."), null=True, ) timer_stop_dt = models.DateTimeField( _("stop date/time"), blank=True, help_text=_("Date and time the timer was stopped."), null=True ) class Meta: abstract = True @property def elapsed_duration(self): """Get the elapsed duration either as the current ``timer_elapsed_seconds`` or by calling ``get_elapsed_duration()``. :rtype: timedelta """ if self.timer_elapsed_seconds: return timedelta(seconds=self.timer_elapsed_seconds) return self.get_elapsed_duration()
[docs] def get_elapsed_duration(self): """Get the time that has elapsed as a duration. :rtype: timedelta """ return timedelta(seconds=self.get_timer_elapsed_seconds())
[docs] def get_elapsed_duration_display(self): """Get a human-friendly display for the duration. :rtype: str """ d = self.get_elapsed_duration() total_seconds = d.seconds if total_seconds == 0: return "" hours, remainder = divmod(total_seconds, 3600) minutes, seconds = divmod(remainder, 60) s = list() if hours > 0: s.append(str(hours)) if hours == 1: s.append(str(_("hour"))) else: s.append(str(_("hours"))) if minutes > 0: s.append(str(minutes)) if minutes == 1: s.append(str(_("minute"))) else: s.append(str(_("minutes"))) return " ".join(s)
[docs] def get_timer_elapsed_seconds(self): """Get the number of seconds that have passed since the timer started until now *or* until the item ended, if a stop datetime is available. :rtype: int This method will: - Calculate the elapsed seconds from the start datetime until the current datetime. - Also include the elapsed seconds already calculated. """ if not self.timer_start_dt: return 0 current_dt = now() delta = current_dt - self.timer_start_dt return delta.seconds + (delta.days * SECONDS_PER_DAY) + (self.timer_elapsed_seconds or 0)
[docs] def reset_timer(self, user, commit=True): """Reset the current timer. :param user: The user updating the record. If the record is an audit model, the ``audit()`` method will be called. Pass ``None`` to suppress this behavior. :type user: AUTH_USER_MODEL | None :param commit: Indicates whether the record should be saved after resetting the timer. :type commit: bool :rtype: int :returns: The value of elapsed seconds prior to the reset. This method will: - Remove the stop datetime and set elapsed seconds to ``0``. - Change the start datetime to the current datetime. - Ensure that the timer is still running. """ current_elapsed_seconds = self.timer_elapsed_seconds or 0 self.timer_elapsed_seconds = 0 self.timer_is_running = True self.timer_stop_dt = None self.timer_start_dt = now() if is_audit_model(self) and user is not None: # noinspection PyUnresolvedReferences self.audit(user, commit=commit) return current_elapsed_seconds if commit: self.save(update_fields=["timer_elapsed_seconds", "timer_is_running", "timer_stop_dt", "timer_start_dt"]) return current_elapsed_seconds
[docs] def start_timer(self, user, commit=True): """Start the timer. :param user: The user updating the record. If the record is an audit model, the ``audit()`` method will be called. Pass ``None`` to suppress this behavior. :type user: AUTH_USER_MODEL | None :param commit: Indicates whether the record should be saved after starting the timer. :type commit: bool :rtype: int :returns: The current elapsed seconds if the timer is already running. Otherwise, ``0``. This method will: - Set the start datetime to the current datetime. - Ensure that the time is running. - Leave the elapsed seconds unchanged. """ if self.timer_is_running: return self.get_timer_elapsed_seconds() self.timer_is_running = True self.timer_start_dt = now() if is_audit_model(self) and user is not None: # noinspection PyUnresolvedReferences self.audit(user, commit=commit) return 0 if commit: self.save(update_fields=["timer_is_running", "timer_start_dt"]) return 0
[docs] def stop_timer(self, user, commit=True): """Stop the timer. :param user: The user updating the record. If the record is an audit model, the ``audit()`` method will be called. Pass ``None`` to suppress this behavior. :type user: AUTH_USER_MODEL | None :param commit: Indicates whether the record should be saved after stopping the timer. :type commit: bool :rtype: int :returns: The elpased seconds. This method will: - Set the stop datetime to the current datetime. - Update the elapsed seconds. - Ensure that the time is not running. """ if not self.timer_is_running: return self.get_timer_elapsed_seconds() self.timer_elapsed_seconds = self.get_timer_elapsed_seconds() self.timer_is_running = False self.timer_stop_dt = now() if is_audit_model(self) and user is not None: # noinspection PyUnresolvedReferences self.audit(user, commit=commit) return self.timer_elapsed_seconds if commit: self.save(update_fields=["timer_elapsed_seconds", "timer_is_running", "timer_stop_dt"]) return self.timer_elapsed_seconds