Skip to content

Commit 9108aa8

Browse files
committed
[IMP] runbot: add kanban view and stages to build error page
Introduce a kanban view to the runbot build error records. Kanban cards show the most valuable field with relevant icons. The concept of "stage" was also introduced for the build error model. Currently there is 4 stages: 1. New: default stage for any new error build 2. Solved: when the issue should be solved 3. Ignored: build error currently ignored (test-tags) 4. Done: When the issue stop happening Certain stages change automatically based as handled in a cron Generally, any not-ignored on-going task end up in done after some period of time if it is not seen for some time The records also get eventually archived (field "Open (not fixed)") after some time. ( see full details in function `_update_stage`). The other stage are expected to be used by the user. For instance after fixing an underteministic error the user can change the stage from new -> solved so that it is moved to done if the build error is not seen for 7 days (instead of 15 if it was in new).
1 parent 3cf9dd6 commit 9108aa8

File tree

7 files changed

+155
-3
lines changed

7 files changed

+155
-3
lines changed

runbot/__manifest__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,20 @@
66
'author': "Odoo SA",
77
'website': "http://runbot.odoo.com",
88
'category': 'Website',
9-
'version': '5.9',
9+
'version': '5.10',
1010
'application': True,
1111
'depends': ['base', 'base_automation', 'website'],
1212
'data': [
1313
'templates/dockerfile.xml',
1414
'data/dockerfile_data.xml',
15+
'data/build_error_stage.xml',
1516
'data/build_parse.xml',
1617
'data/error_link.xml',
1718
'data/runbot_build_config_data.xml',
1819
'data/runbot_data.xml',
1920
'data/runbot_error_regex_data.xml',
2021
'data/website_data.xml',
22+
'data/ir_cron_data.xml',
2123

2224
'security/runbot_security.xml',
2325
'security/ir.model.access.csv',

runbot/data/build_error_stage.xml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<odoo>
2+
<record model="runbot.build.error.stage" id="build_error_stage_new">
3+
<field name="name">New/Unsolved</field>
4+
<field name="sequence">5</field>
5+
<field name="description">New build error detected by the runbot platform</field>
6+
</record>
7+
<record model="runbot.build.error.stage" id="build_error_stage_solved">
8+
<field name="name">Solved</field>
9+
<field name="sequence">10</field>
10+
<field name="description">Issue should be solved. Will automatically move to the Done state after some time</field>
11+
</record>
12+
<record model="runbot.build.error.stage" id="build_error_stage_ignored">
13+
<field name="name">Ignored</field>
14+
<field name="sequence">15</field>
15+
<field name="description">Issue can be ignored, but should eventually be solved (for instance, test-tags)</field>
16+
<field name="fold">True</field>
17+
</record>
18+
<record model="runbot.build.error.stage" id="build_error_stage_done">
19+
<field name="name">Done</field>
20+
<field name="sequence">20</field>
21+
<field name="description">Issue is solved or dissapeared</field>
22+
<field name="fold">True</field>
23+
</record>
24+
</odoo>

runbot/data/ir_cron_data.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<odoo>
2+
<record id="runbot_update_build_errors_stage" model="ir.cron">
3+
<field name="name">Runbot: Update Build Errors Stage</field>
4+
<field name="model_id" ref="model_runbot_build_error"/>
5+
<field name="state">code</field>
6+
<field name="code">model._update_stage()</field>
7+
<field name="interval_number">1</field>
8+
<field name="interval_type">days</field>
9+
</record>
10+
</odoo>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
from odoo import api, SUPERUSER_ID
3+
4+
5+
def migrate(cr, version):
6+
env = api.Environment(cr, SUPERUSER_ID, {})
7+
# By default, the build_error_stage is set to the new stage
8+
build_error_stage_ignored = env.ref('runbot.build_error_stage_ignored').id
9+
build_error_stage_done = env.ref('runbot.build_error_stage_done').id
10+
11+
# Archived build errors are set are considered Done
12+
env['runbot.build.error'].search([['active', "=", False]]).write({'stage_id': build_error_stage_done})
13+
# Build errors with test_tags are set to Ignored
14+
env['runbot.build.error'].search([['test_tags', '!=', False]]).write({'stage_id': build_error_stage_ignored})

runbot/models/build_error.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,18 @@ def _search(self, operator, value):
9090
return [(f'error_content_ids.{field_name}', operator, value)]
9191
return _search
9292

93+
94+
class BuildErrorStage(models.Model):
95+
_name = 'runbot.build.error.stage'
96+
_description = 'Build Error Stage'
97+
_order = 'sequence'
98+
99+
name = fields.Char(string='Stage Name', required=True)
100+
description = fields.Text(string='Stage description')
101+
sequence = fields.Integer('Sequence', default=1)
102+
fold = fields.Boolean(string='Folded in Kanban', default=False)
103+
104+
93105
class BuildError(models.Model):
94106
_name = "runbot.build.error"
95107
_description = "Build error"
@@ -105,6 +117,7 @@ class BuildError(models.Model):
105117
error_count = fields.Integer("Error count", store=True, compute='_compute_count')
106118
previous_error_id = fields.Many2one('runbot.build.error', string="Already seen error")
107119

120+
stage_id = fields.Many2one('runbot.build.error.stage', required=True, tracking=True, group_expand='_read_group_expand_full', default=lambda self: self.env['runbot.build.error.stage'].search([], limit=1))
108121
responsible = fields.Many2one('res.users', 'Assigned fixer', tracking=True)
109122
customer = fields.Many2one('res.users', 'Customer', tracking=True)
110123
team_id = fields.Many2one('runbot.team', 'Assigned team', tracking=True)
@@ -347,6 +360,7 @@ def _compute_graph(self):
347360

348361
@api.constrains('test_tags')
349362
def _check_test_tags(self):
363+
self.stage_id = self.env.ref('runbot.build_error_stage_ignored').id
350364
for build_error in self:
351365
if build_error.test_tags and '-' in build_error.test_tags:
352366
raise ValidationError('Build error test_tags should not be negated')
@@ -586,6 +600,37 @@ def action_link_errors(self):
586600
base_error = self_sorted[0]
587601
base_error._merge(self_sorted - base_error)
588602

603+
def _update_stage(self, nbr_day_solved_to_done=7, nbr_day_new_to_done=15, nbr_day_done_to_archive=30):
604+
"""Called automatically by scheduled action to update the stage of the error if necessary"""
605+
now = fields.Datetime.now()
606+
build_error_stage_new = self.env.ref('runbot.build_error_stage_new').id
607+
build_error_stage_solved = self.env.ref('runbot.build_error_stage_solved').id
608+
build_error_stage_done = self.env.ref('runbot.build_error_stage_done').id
609+
610+
# Very old done error eventually get archived
611+
self.search([
612+
('stage_id', '=', build_error_stage_done),
613+
('last_seen_date', '<', now - relativedelta(days=nbr_day_done_to_archive)),
614+
]).write({'active': False})
615+
616+
# Done errors that did happen again recently are moved back to new
617+
self.search([
618+
('stage_id', '=', build_error_stage_done),
619+
('last_seen_date', '>=', now - relativedelta(days=nbr_day_new_to_done)),
620+
]).write({'stage_id': build_error_stage_new})
621+
622+
# New error that did not appear after a long time are marked as done
623+
# Solved error that did not appear after a short time are marked as done
624+
self.search([
625+
'|',
626+
'&',
627+
('stage_id', '=', build_error_stage_new),
628+
('last_seen_date', '<', now - relativedelta(days=nbr_day_new_to_done)),
629+
'&',
630+
('stage_id', '=', build_error_stage_solved),
631+
('last_seen_date', '<', now - relativedelta(days=nbr_day_solved_to_done)),
632+
]).write({'stage_id': build_error_stage_done})
633+
589634

590635
class BuildErrorContent(models.Model):
591636

runbot/security/ir.model.access.csv

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,4 +151,5 @@ access_runbot_build_stat_regex_wizard,access_runbot_build_stat_regex_wizard,mode
151151

152152
access_runbot_host_message,access_runbot_host_message,runbot.model_runbot_host_message,runbot.group_runbot_admin,1,0,0,0
153153

154-
154+
access_runbot_build_error_stage_user,access_runbot_build_error_stage_user,runbot.model_runbot_build_error_stage,base.group_user,1,0,0,0
155+
access_runbot_build_error_stage_admin,access_runbot_build_error_stage_admin,runbot.model_runbot_build_error_stage,base.group_user,1,1,1,1

runbot/views/build_error_views.xml

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
<field name="model">runbot.build.error</field>
66
<field name="arch" type="xml">
77
<form>
8+
<header>
9+
<field name="stage_id" widget="statusbar" options="{'clickable': '1'}"/>
10+
</header>
811
<sheet>
912
<div name="button_box">
1013
<button class="oe_stat_button" type="object" icon="fa-exclamation-circle" name="action_get_build_link_record">
@@ -329,6 +332,59 @@
329332
<field name="binding_view_types">list</field>
330333
</record>
331334

335+
<record id="build_error_view_kanban" model="ir.ui.view">
336+
<field name="name">runbot.build.error.kanban</field>
337+
<field name="model">runbot.build.error</field>
338+
<field name="arch" type="xml">
339+
<kanban default_group_by="stage_id" quick_create="false" default_order="last_seen_date desc">
340+
<templates>
341+
<t t-name="card">
342+
<widget name="web_ribbon" title="Test-tags" bg_color="bg-danger" invisible="not test_tags"/>
343+
<field name="name" class="fw-bold fs-5"/>
344+
<group>
345+
<div style="display: flex; align-items: center;">
346+
<i class="fa fa-clock-o me-2" title="Date interval from first seen to last seen"/>
347+
<field name="first_seen_date" widget="remaining_days"/>
348+
<i class="fa fa-long-arrow-right mx-2 oe_read_only" title="to"/>
349+
<field name="last_seen_date" widget="remaining_days"/>
350+
</div>
351+
352+
<div class="d-flex align-items-center gap-1">
353+
<i class="fa fa-repeat" title="Number of occurence"/>
354+
<field name="error_count"/>
355+
</div>
356+
357+
<div class="d-flex align-items-center gap-1" style="display: flex; align-items: center;">
358+
<i class="fa fa-code-fork" title="Concerned Odoo versions"/>
359+
<field name="version_ids" widget="many2many_tags"/>
360+
</div>
361+
<div class="d-flex align-items-center gap-1" style="display: flex; align-items: center;">
362+
<i class="fa fa-bullseye" title="Triggers"/>
363+
<field name="trigger_ids" widget="many2many_tags"/>
364+
</div>
365+
</group>
366+
367+
<footer>
368+
<div class="d-flex align-items-center gap-1">
369+
<field name="activity_ids" widget="kanban_activity"/>
370+
</div>
371+
<div class="d-flex align-items-center gap-1 ms-auto">
372+
<i class="fa fa-random text-danger" title="inconsistant" invisible="not random"/>
373+
<i class="fa fa-users" title="Responsible team"/>
374+
<field name="team_id"/> <i t-if="!record.team_id.raw_value">no team</i>
375+
<i class="fa fa-address-card" title="Investigator"/>
376+
<field name="customer" widget="many2one_avatar_user"/>
377+
<i class="fa fa-wrench" title="Solver"/>
378+
<field name="responsible" widget="many2one_avatar_user"/>
379+
<field name="fixing_pr_url" widget="url" text="PR" invisible="not fixing_pr_url"/>
380+
</div>
381+
</footer>
382+
</t>
383+
</templates>
384+
</kanban>
385+
</field>
386+
</record>
387+
332388
<record id="build_error_view_tree" model="ir.ui.view">
333389
<field name="name">runbot.build.error.list</field>
334390
<field name="model">runbot.build.error</field>
@@ -478,7 +534,7 @@
478534
<field name="name">Errors</field>
479535
<field name="res_model">runbot.build.error</field>
480536
<field name="path">error</field>
481-
<field name="view_mode">list,form</field>
537+
<field name="view_mode">kanban,list,form</field>
482538
<field name="context">{'search_default_not_fixed_errors': True, 'active_test': False}</field>
483539
</record>
484540

0 commit comments

Comments
 (0)