diff --git a/runbot/common.py b/runbot/common.py index 6c33b548..328d078e 100644 --- a/runbot/common.py +++ b/runbot/common.py @@ -12,7 +12,7 @@ import os from collections import OrderedDict from datetime import timedelta -from babel.dates import format_timedelta +from babel.dates import LC_TIME, Locale, TIMEDELTA_UNITS from markupsafe import Markup from odoo.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT, html_escape, file_open @@ -80,11 +80,61 @@ def time_delta(time): return time return timedelta(seconds=-time) +from babel.dates import format_timedelta as _format_timedelta + + +def format_timedelta(delta, granularity='second', max_unit=None, threshold=.85, + add_direction=False, format='long', + locale=LC_TIME): + """ + Modified version of Dates.format_timedelta + """ + if format not in ('narrow', 'short', 'long'): + raise TypeError('Format must be one of "narrow", "short" or "long"') + if isinstance(delta, timedelta): + seconds = int((delta.days * 86400) + delta.seconds) + else: + seconds = delta + locale = Locale.parse(locale) + + def _iter_patterns(a_unit): + if add_direction: + unit_rel_patterns = locale._data['date_fields'][a_unit] + if seconds >= 0: + yield unit_rel_patterns['future'] + else: + yield unit_rel_patterns['past'] + a_unit = 'duration-' + a_unit + yield locale._data['unit_patterns'].get(a_unit, {}).get(format) + + for unit, secs_per_unit in TIMEDELTA_UNITS: + if max_unit and unit != max_unit: + continue + max_unit = None + value = abs(seconds) / secs_per_unit + if value >= threshold or unit == granularity: + if unit == granularity and value > 0: + value = max(1, value) + value = int(round(value)) + plural_form = locale.plural_form(value) + pattern = None + for patterns in _iter_patterns(unit): + if patterns is not None: + pattern = patterns[plural_form] + break + # This really should not happen + if pattern is None: + return u'' + return pattern.replace('{0}', str(value)) + + return u'' + def s2human(time): """Convert a time in second into an human readable string""" return format_timedelta( time_delta(time), + max_unit='hour', format="narrow", threshold=2.1, ) diff --git a/runbot/models/build.py b/runbot/models/build.py index 2b7872f3..5f232332 100644 --- a/runbot/models/build.py +++ b/runbot/models/build.py @@ -190,6 +190,9 @@ class BuildResult(models.Model): docker_start = fields.Datetime('Docker start') job_time = fields.Integer(compute='_compute_job_time', string='Job time') build_time = fields.Integer(compute='_compute_build_time', string='Build time') + wait_time = fields.Integer(compute='_compute_wait_time', string='Wait time') + load_time = fields.Integer(compute='_compute_load_time', string='Load time') + last_update = fields.Datetime(compute='_compute_last_update', string='Last update') gc_date = fields.Datetime('Local cleanup date', compute='_compute_gc_date') gc_delay = fields.Integer('Cleanup Delay', help='Used to compute gc_date') @@ -465,6 +468,24 @@ class BuildResult(models.Model): else: build.build_time = 0 + #@api.depends('create_date', 'last_update') + def _compute_wait_time(self): + for build in self: + build.wait_time = dt2time(build.last_update) - dt2time(build.create_date) + + #@api.depends('build_end', 'children_ids.last_update') + def _compute_last_update(self): + for build in self: + if not build.build_end: + build.last_update = datetime.datetime.now() + else: + build.last_update = max([child.last_update for child in build.children_ids] + [build.build_end]) + + #@api.depends('build_time', 'children_ids.load_time') + def _compute_load_time(self): + for build in self: + build.load_time = sum([build.build_time] + [child.load_time for child in build.children_ids]) + @api.depends('job_start') def _compute_build_age(self): """Return the time between job start and now""" @@ -1163,9 +1184,6 @@ class BuildResult(models.Model): self._log('write_file', 'exception: %s' % e) return False - def _get_formated_build_time(self): - return s2human(self.build_time) - def _get_color_class(self): if self.global_result == 'ko': diff --git a/runbot/models/build_config.py b/runbot/models/build_config.py index f13da2e7..64ce96ea 100644 --- a/runbot/models/build_config.py +++ b/runbot/models/build_config.py @@ -325,7 +325,7 @@ class ConfigStep(models.Model): if not self: return False self.ensure_one() - return self.job_type in ('install_odoo', 'run_odoo', 'restore', 'test_upgrade') or (self.job_type == 'python' and ('docker_params =' in self.python_code or '_run_' in self.python_code)) + return self.job_type in ('install_odoo', 'run_odoo', 'restore', 'test_upgrade') or (self.job_type == 'python' and ('docker_params =' in self.python_code or '_run_' in self.python_code or 'cmd' in self.python_code)) def _run_run_odoo(self, build, force=False): if not force: diff --git a/runbot/templates/batch.xml b/runbot/templates/batch.xml index 8c6cf035..957eb756 100644 --- a/runbot/templates/batch.xml +++ b/runbot/templates/batch.xml @@ -11,7 +11,7 @@