Changeset - 0e271127bb62
[Not reviewed]
0 2 0
Lance Edgar (lance) - 2 months ago 2024-08-09 17:00:47
lance@edbob.org
fix: add `rattail.util.render_duration()` function

so this can be called outside of app handler context, specifically
from a colander schema node
2 files changed with 49 insertions and 35 deletions:
0 comments (0 inline, 0 general)
rattail/app.py
Show inline comments
 
@@ -48,6 +48,7 @@ from wuttjamaican.app import (AppHandler as WuttaAppHandler,
 
from rattail.util import (load_entry_points,
 
                          progress_loop, prettify,
 
                          pretty_quantity,
 
                          render_duration,
 
                          NOTSET)
 
from rattail.files import temp_path, resource_path
 
from rattail.mail import send_email
 
@@ -2002,43 +2003,15 @@ class AppHandler(WuttaAppHandler):
 
        # but if fallback specified, use that
 
        return fallback
 

	
 
    def render_duration(self, delta=None, hours=None, seconds=None, **kwargs):
 
    def render_duration(self, **kwargs):
 
        """
 
        Render a time duration for human eyes, e.g. "1:30" for 1.5 hours.
 

	
 
        Note that you must specify either ``delta``, ``hours`` or ``seconds``.
 

	
 
        :param delta: If specified, should be a ``datetime.timedelta``
 
           object representing the duration.
 

	
 
        :param hours: If specified, should be the number of hours
 
           elapsed for the duration, as decimal.
 

	
 
        :param seconds: If specified, should be the number of seconds
 
           elapsed for the duration, as integer.
 

	
 
        :returns: Duration rendered as human-friendly string.  Note
 
           that if all params are empty, this will return an empty
 
           string.
 
        This is a convenience wrapper around
 
        :func:`rattail.util.render_duration()`; please see that for
 
        more info.
 
        """
 
        if delta is None and hours is None and seconds is None:
 
            return ""
 

	
 
        if delta is None:
 
            if seconds is None:
 
                seconds = int(hours * 60 * 60)
 
            delta = datetime.timedelta(seconds=seconds)
 

	
 
        # calculate minutes for the 'seconds' portion of delta.  note
 
        # that we must ensure it is correctly rounded
 
        minutes = delta.seconds / 60
 
        minutes = decimal.Decimal(str(minutes)).quantize(decimal.Decimal('1'))
 

	
 
        # now add in minutes for the 'days' portion of delta
 
        minutes += delta.days * 24 * 60
 

	
 
        # calculate/render final string
 
        return "{}:{:02d}".format(int(minutes // 60), int(minutes % 60))
 
        return render_duration(**kwargs)
 

	
 
    def render_percent(self, value, places=2, from_decimal=False,
 
                       **kwargs):
rattail/util.py
Show inline comments
 
@@ -2,7 +2,7 @@
 
################################################################################
 
#
 
#  Rattail -- Retail Software Framework
 
#  Copyright © 2010-2023 Lance Edgar
 
#  Copyright © 2010-2024 Lance Edgar
 
#
 
#  This file is part of Rattail.
 
#
 
@@ -49,7 +49,7 @@ class OrderedDict(collections.OrderedDict):
 
        warnings.warn("rattail.util.OrderedDict is deprecated; "
 
                      "please use collections.OrderedDict instead",
 
                      DeprecationWarning, stacklevel=2)
 
        super(OrderedDict, self).__init__(*args, **kwargs)
 
        super().__init__(*args, **kwargs)
 

	
 

	
 
def capture_output(*args, **kwargs):
 
@@ -353,6 +353,47 @@ def pretty_quantity(value, empty_zero=False):
 
    return str(value).rstrip('0')
 

	
 

	
 
def render_duration(delta=None, hours=None, seconds=None):
 
    """
 
    Render a time duration for human eyes, e.g. "1:30" for 1.5 hours.
 

	
 
    Note that you must specify one of: ``delta``, ``hours``, ``seconds``.
 

	
 
    :param delta: If specified, should be a ``datetime.timedelta``
 
       object representing the duration.
 

	
 
    :param hours: If specified, should be the number of hours
 
       elapsed for the duration, as decimal.
 

	
 
    :param seconds: If specified, should be the number of seconds
 
       elapsed for the duration, as integer.
 

	
 
    :returns: Duration rendered as human-friendly string.  Note
 
       that if all params are empty, this will return an empty
 
       string.
 
    """
 
    if delta is None and hours is None and seconds is None:
 
        return ""
 

	
 
    if delta is None:
 
        if seconds is None:
 
            seconds = int(hours * 60 * 60)
 
        delta = datetime.timedelta(seconds=seconds)
 

	
 
    # calculate minutes for the 'seconds' portion of delta.  note
 
    # that we must ensure it is correctly rounded
 
    minutes = delta.seconds / 60
 
    minutes = decimal.Decimal(str(minutes)).quantize(decimal.Decimal('1'))
 

	
 
    # now add in minutes for the 'days' portion of delta
 
    minutes += delta.days * 24 * 60
 

	
 
    # calculate/render final string
 
    hours = int(minutes // 60)
 
    minutes = int(minutes % 60)
 
    return f"{hours}:{minutes:02d}"
 

	
 

	
 
def progress_loop(func, items, factory, *args, **kwargs):
 
    """
 
    This will iterate over ``items`` and call ``func`` for each.  If a progress
0 comments (0 inline, 0 general)