[IMP] runbot: add total load time on builds

This commit is contained in:
Xavier-Do 2024-02-15 16:55:16 +01:00 committed by Christophe Monniez
parent 336e9525d5
commit 60dbbcb72e
8 changed files with 109 additions and 22 deletions

View File

@ -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,
)

View File

@ -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':

View File

@ -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:

View File

@ -11,7 +11,7 @@
<td>
<a t-esc="batch.bundle_id.name" t-attf-href="/runbot/bundle/{{batch.bundle_id.id}}"/>
&amp;emsp;
<a groups="runbot.group_runbot_advanced_user" t-attf-href="/web/#id={{batch.id}}&amp;view_type=form&amp;model=runbot.batch&amp;menu_id={{env.ref('runbot.runbot_menu_root').id}}" class="btn btn-default btn-sm" target="new" title="View Batch in Backend">
<a groups="runbot.group_runbot_advanced_user" t-attf-href="/web/#id={{batch.id}}&amp;view_type=form&amp;model=runbot.batch&amp;menu_id={{env['ir.model.data']._xmlid_to_res_id('runbot.runbot_menu_root')}}" class="btn btn-default btn-sm" target="new" title="View Batch in Backend">
<i class="fa fa-list"/>
</a>
</td>

View File

@ -156,16 +156,35 @@
</t>
<b>Host:</b>
<t t-esc="build.host"/>
<br/>
</t>
<b>Total time:</b>
<t t-esc="build._get_formated_build_time()"/>
<br/>
<t t-if="build.stat_ids">
<b>Stats:</b>
<a t-attf-href="/runbot/build/stats/{{build.id}}">Build <t t-esc="build.id"/></a>
<br/>
</t>
<div>
<b title="Execution time of this build, without child time">
Build time:
</b>
<t t-att-tile='build.build_time' t-esc="s2human(build.build_time)"/>
<i t-if='more'>(<t t-esc="build.build_time"/>s)</i>
</div>
<div>
<b title='Time from creation to finish (queue time + completion time)'>
Wait time:
</b>
<t t-att-tile='build.wait_time' t-esc="s2human(build.wait_time)"/>
<i t-if='more'>(<t t-esc="build.wait_time"/>s)</i>
</div>
<div>
<b title='Total time '>
Load time:
</b>
<t t-att-tile='build.load_time' t-esc="s2human(build.load_time)"/>
<i t-if='more'>(<t t-esc="build.load_time"/>s)</i>
</div>
<div>
<t t-if="build.stat_ids">
<b>Stats:</b>
<a t-attf-href="/runbot/build/stats/{{build.id}}">Build <t t-esc="build.id"/></a>
<br/>
</t>
</div>
</div>
</div>
<div class="col-md-6" t-if="build.children_ids">
@ -204,7 +223,7 @@
</t>
</td>
<td>
<span t-attf-class="badge badge-info" t-esc="child._get_formated_build_time()"/>
<span t-attf-class="badge badge-info" t-esc="s2human(child.build_time)"/>
</td>
<td>
<t t-call="runbot.build_button">
@ -336,7 +355,7 @@
<td/><td/><td/>
<td t-attf-class="bg-{{'info' if error.active else 'success'}}-light {{size}}" colspan="2">
This error is already <a href="#" t-attf-title="{{'Was detected by runbot in nightly builds.' if error.active else 'Either the error is not properly fixed or the branch does not contain the fix.'}}"><t t-esc="'known' if error.active else 'fixed'"/></a>.
<a groups="runbot.group_user" t-attf-href="/web#id={{l.error_id.id}}&amp;view_type=form&amp;model=runbot.build.error&amp;menu_id={{env.ref('runbot.runbot_menu_root').id}}" title="View in Backend" target="new">
<a groups="runbot.group_user" t-attf-href="/web#id={{l.error_id.id}}&amp;view_type=form&amp;model=runbot.build.error&amp;menu_id={{env['ir.model.data']._xmlid_to_res_id('runbot.runbot_menu_root')}}" title="View in Backend" target="new">
<i t-attf-class="fa fa-{{icon}}"/>
</a>
<span groups="runbot.group_runbot_admin" t-if="error.responsible or error.responsible.id == uid">(<i t-esc="error.responsible.name"/>)</span>
@ -387,7 +406,7 @@
<span t-esc="build.params_id.version_id.name"/>
</td>
<td>
<span t-esc="build._get_formated_build_time()"/>
<span t-esc="s2human(build.build_time)"/>
</td>
<td>
<t t-call="runbot.build_button">

View File

@ -38,7 +38,7 @@
</div>
<div class="col">
<a t-att-href="build_error.last_seen_build_id.build_url" t-attf-title="View last affected build ({{build_error.last_seen_build_id.id}})"><i class="fa fa-external-link"/></a>
<a groups="base.group_user" t-attf-href="/web/#id={{build_error.id}}&amp;view_type=form&amp;model=runbot.build.error&amp;menu_id={{env.ref('runbot.runbot_menu_root').id}}" target="new" title="View in Backend">
<a groups="base.group_user" t-attf-href="/web/#id={{build_error.id}}&amp;view_type=form&amp;model=runbot.build.error&amp;menu_id={{env['ir.model.data']._xmlid_to_res_id('runbot.runbot_menu_root')}}" target="new" title="View in Backend">
<span class="badge badge-info" t-esc="build_error.build_count" t-attf-title="This error was seen {{build_error.build_count}} View in backend"/>
</a>
<span groups="!base.group_user" class="badge badge-info" t-esc="build_error.build_count" t-attf-title="This error was seen {{build_error.build_count}}"/>
@ -114,7 +114,7 @@
<div t-if="team" class='col-md-12'>
<div class="col-lg-12 text-center mb16">
<h2>Team <t t-esc="team.name.capitalize()"/>
<a groups="base.group_user" t-attf-href="/web/#id={{team.id}}&amp;view_type=form&amp;model=runbot.team&amp;menu_id={{env.ref('runbot.runbot_menu_root').id}}" target="new" title="View in Backend">
<a groups="base.group_user" t-attf-href="/web/#id={{team.id}}&amp;view_type=form&amp;model=runbot.team&amp;menu_id={{env['ir.model.data']._xmlid_to_res_id('runbot.runbot_menu_root')}}" target="new" title="View in Backend">
<i class="fa fa-list"/>
</a>
</h2>

View File

@ -11,7 +11,7 @@
<t t-esc="bundle.name"/>
<i t-if="bundle.sticky" class="fa fa-star" style="color: #f0ad4e" />
<div class="btn-group" role="group">
<a groups="runbot.group_runbot_advanced_user" t-attf-href="/web/#id={{bundle.id}}&amp;view_type=form&amp;model=runbot.bundle&amp;menu_id={{env.ref('runbot.runbot_menu_root').id}}" class="btn btn-default btn-sm" target="new" title="View in Backend">
<a groups="runbot.group_runbot_advanced_user" t-attf-href="/web/#id={{bundle.id}}&amp;view_type=form&amp;model=runbot.bundle&amp;menu_id={{env['ir.model.data']._xmlid_to_res_id('runbot.runbot_menu_root')}}" class="btn btn-default btn-sm" target="new" title="View in Backend">
<i class="fa fa-list"/>
</a>
<a groups="runbot.group_runbot_advanced_user" class="btn btn-default" t-attf-href="/runbot/bundle/{{bundle.id}}/force" title="Force A New Batch">

View File

@ -325,7 +325,7 @@
<i class="fa fa-search"/>
Find similar builds
</a>
<a class="dropdown-item" t-attf-href="/web/#id={{bu['id']}}&amp;view_type=form&amp;model=runbot.build&amp;menu_id={{env.ref('runbot.runbot_menu_root').id}}" target="new">
<a class="dropdown-item" t-attf-href="/web/#id={{bu['id']}}&amp;view_type=form&amp;model=runbot.build&amp;menu_id={{env['ir.model.data']._xmlid_to_res_id('runbot.runbot_menu_root')}}" target="new">
<i class="fa fa-list"/>
View in backend
</a>