From 2cbbc88dabcbe11baca2f3e30ec7425af39141e4 Mon Sep 17 00:00:00 2001 From: jem-odoo Date: Wed, 19 Jun 2019 13:27:47 +0000 Subject: [PATCH] [IMP] tools: introduces format_duration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As some formatting tools already exists, it is time for float_time conversion to have its own. Purpose is to extract formatting logic from the float_time qweb widget to tools, in order to reuse it in mail.template (jinja). We named it 'format_duration', to follow babel convention. This can bring confusion with the duration qweb widget, but this widget should be called 'timedelta', as it is more generic than this one. Task-2025396 closes odoo/odoo#34417 Signed-off-by: Jérome Maes (jem) --- addons/mail/models/mail_template.py | 1 + odoo/addons/base/models/ir_qweb_fields.py | 10 ++------- odoo/tools/misc.py | 26 +++++++++++++++++------ 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/addons/mail/models/mail_template.py b/addons/mail/models/mail_template.py index 3edfbe28e66..d92a8a5ecb0 100644 --- a/addons/mail/models/mail_template.py +++ b/addons/mail/models/mail_template.py @@ -281,6 +281,7 @@ class MailTemplate(models.Model): 'format_date': lambda date, date_format=False, lang_code=False: format_date(self.env, date, date_format, lang_code), 'format_datetime': lambda dt, tz=False, dt_format=False, lang_code=False: format_datetime(self.env, dt, tz, dt_format, lang_code), 'format_amount': lambda amount, currency, lang_code=False: tools.format_amount(self.env, amount, currency, lang_code), + 'format_duration': lambda value: tools.format_duration(value), 'user': self.env.user, 'ctx': self._context, # context kw would clash with mako internals } diff --git a/odoo/addons/base/models/ir_qweb_fields.py b/odoo/addons/base/models/ir_qweb_fields.py index 57e7f3891f5..8b157914ea2 100644 --- a/odoo/addons/base/models/ir_qweb_fields.py +++ b/odoo/addons/base/models/ir_qweb_fields.py @@ -9,7 +9,7 @@ import babel from lxml import etree import math -from odoo.tools import html_escape as escape, posix_to_ldml, safe_eval, float_utils, format_date, pycompat +from odoo.tools import html_escape as escape, posix_to_ldml, safe_eval, float_utils, format_date, format_duration, pycompat import logging _logger = logging.getLogger(__name__) @@ -480,13 +480,7 @@ class FloatTimeConverter(models.AbstractModel): @api.model def value_to_html(self, value, options): - sign = math.copysign(1.0, value) - hours, minutes = divmod(abs(value) * 60, 60) - minutes = round(minutes) - if minutes == 60: - minutes = 0 - hours += 1 - return '%02d:%02d' % (sign * hours, minutes) + return format_duration(value) class DurationConverter(models.AbstractModel): diff --git a/odoo/tools/misc.py b/odoo/tools/misc.py index c8bb5cb8a3a..96395075de5 100644 --- a/odoo/tools/misc.py +++ b/odoo/tools/misc.py @@ -9,6 +9,7 @@ from functools import wraps import babel from contextlib import contextmanager import datetime +import math import subprocess import io import os @@ -43,7 +44,7 @@ except ImportError: from .config import config from .cache import * -from .parse_version import parse_version +from .parse_version import parse_version from . import pycompat import odoo @@ -144,7 +145,7 @@ def file_open(name, mode="r", subdir='addons', pathinfo=False): """Open a file from the OpenERP root, using a subdir folder. Example:: - + >>> file_open('hr/report/timesheer.xsl') >>> file_open('addons/hr/report/timesheet.xsl') @@ -290,7 +291,7 @@ def flatten(list): def reverse_enumerate(l): """Like enumerate but in the other direction - + Usage:: >>> a = ['a', 'b', 'c'] >>> it = reverse_enumerate(a) @@ -676,10 +677,10 @@ def posix_to_ldml(fmt, locale): def split_every(n, iterable, piece_maker=tuple): """Splits an iterable into length-n pieces. The last piece will be shorter if ``n`` does not evenly divide the iterable length. - + :param int n: maximum size of each generated chunk :param Iterable iterable: iterable to chunk into pieces - :param piece_maker: callable taking an iterable and collecting each + :param piece_maker: callable taking an iterable and collecting each chunk from its slice, *must consume the entire slice*. """ iterator = iter(iterable) @@ -754,7 +755,7 @@ class unquote(str): return self class UnquoteEvalContext(defaultdict): - """Defaultdict-based evaluation context that returns + """Defaultdict-based evaluation context that returns an ``unquote`` string for any missing name used during the evaluation. Mostly useful for evaluating OpenERP domains/contexts that @@ -1253,6 +1254,19 @@ def format_amount(env, amount, currency, lang_code=False): return u'{pre}{0}{post}'.format(formatted_amount, pre=pre, post=post) +def format_duration(value): + """ Format a float: used to display integral or fractional values as + human-readable time spans (e.g. 1.5 as "01:30"). + """ + sign = math.copysign(1.0, value) + hours, minutes = divmod(abs(value) * 60, 60) + minutes = round(minutes) + if minutes == 60: + minutes = 0 + hours += 1 + return '%02d:%02d' % (sign * hours, minutes) + + def _consteq(str1, str2): """ Constant-time string comparison. Suitable to compare bytestrings of fixed, known length only, because length difference is optimized. """