From e963ffa298b7d0ba7e7e051219c4cda6f0ff31cb Mon Sep 17 00:00:00 2001 From: lse-odoo Date: Fri, 28 Feb 2025 18:12:02 +0100 Subject: [PATCH] [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). --- runbot/__manifest__.py | 4 +- runbot/data/build_error_stage.xml | 24 ++++++++ runbot/data/ir_cron_data.xml | 10 ++++ runbot/migrations/18.0.5.10/post-migration.py | 14 +++++ runbot/models/build_error.py | 38 ++++++++++++ runbot/security/ir.model.access.csv | 3 +- runbot/views/build_error_views.xml | 58 ++++++++++++++++++- 7 files changed, 148 insertions(+), 3 deletions(-) create mode 100644 runbot/data/build_error_stage.xml create mode 100644 runbot/data/ir_cron_data.xml create mode 100644 runbot/migrations/18.0.5.10/post-migration.py diff --git a/runbot/__manifest__.py b/runbot/__manifest__.py index 5085df56..dbcad1d5 100644 --- a/runbot/__manifest__.py +++ b/runbot/__manifest__.py @@ -6,18 +6,20 @@ 'author': "Odoo SA", 'website': "http://runbot.odoo.com", 'category': 'Website', - 'version': '5.9', + 'version': '5.10', 'application': True, 'depends': ['base', 'base_automation', 'website'], 'data': [ 'templates/dockerfile.xml', 'data/dockerfile_data.xml', + 'data/build_error_stage.xml', 'data/build_parse.xml', 'data/error_link.xml', 'data/runbot_build_config_data.xml', 'data/runbot_data.xml', 'data/runbot_error_regex_data.xml', 'data/website_data.xml', + 'data/ir_cron_data.xml', 'security/runbot_security.xml', 'security/ir.model.access.csv', diff --git a/runbot/data/build_error_stage.xml b/runbot/data/build_error_stage.xml new file mode 100644 index 00000000..b98e7b70 --- /dev/null +++ b/runbot/data/build_error_stage.xml @@ -0,0 +1,24 @@ + + + New/Unsolved + 5 + New build error detected by the runbot platform + + + Solved + 10 + Issue should be solved. Will automatically move to the Done state after some time + + + Ignored + 15 + Issue can be ignored, but should eventually be solved (for instance, test-tags) + True + + + Done + 20 + Issue is solved or dissapeared + True + + diff --git a/runbot/data/ir_cron_data.xml b/runbot/data/ir_cron_data.xml new file mode 100644 index 00000000..f55bf3f9 --- /dev/null +++ b/runbot/data/ir_cron_data.xml @@ -0,0 +1,10 @@ + + + Runbot: Update Build Errors Stage + + code + model._update_stage() + 1 + days + + diff --git a/runbot/migrations/18.0.5.10/post-migration.py b/runbot/migrations/18.0.5.10/post-migration.py new file mode 100644 index 00000000..9634646e --- /dev/null +++ b/runbot/migrations/18.0.5.10/post-migration.py @@ -0,0 +1,14 @@ + +from odoo import api, SUPERUSER_ID + + +def migrate(cr, version): + env = api.Environment(cr, SUPERUSER_ID, {}) + # By default, the build_error_stage is set to the new stage + build_error_stage_ignored = env.ref('runbot.build_error_stage_ignored').id + build_error_stage_done = env.ref('runbot.build_error_stage_done').id + + # Archived build errors are set are considered Done + env['runbot.build.error'].search([['active', "=", False]]).write({'stage_id': build_error_stage_done}) + # Build errors with test_tags are set to Ignored + env['runbot.build.error'].search([['test_tags', '!=', False]]).write({'stage_id': build_error_stage_ignored}) diff --git a/runbot/models/build_error.py b/runbot/models/build_error.py index c2eccda4..d689d777 100644 --- a/runbot/models/build_error.py +++ b/runbot/models/build_error.py @@ -90,6 +90,18 @@ def _search_related_error_content_ids(field_name): return [(f'error_content_ids.{field_name}', operator, value)] return _search + +class BuildErrorStage(models.Model): + _name = 'runbot.build.error.stage' + _description = 'Build Error Stage' + _order = 'sequence' + + name = fields.Char(string='Stage Name', required=True) + description = fields.Text(string='Stage description') + sequence = fields.Integer('Sequence', default=1) + fold = fields.Boolean(string='Folded in Kanban', default=False) + + class BuildError(models.Model): _name = "runbot.build.error" _description = "Build error" @@ -105,6 +117,7 @@ class BuildError(models.Model): error_count = fields.Integer("Error count", store=True, compute='_compute_count') previous_error_id = fields.Many2one('runbot.build.error', string="Already seen error") + 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)) responsible = fields.Many2one('res.users', 'Assigned fixer', tracking=True) customer = fields.Many2one('res.users', 'Customer', tracking=True) team_id = fields.Many2one('runbot.team', 'Assigned team', tracking=True) @@ -586,6 +599,31 @@ class BuildError(models.Model): base_error = self_sorted[0] base_error._merge(self_sorted - base_error) + def _update_stage(self, nbr_day_solved_to_done=7, nbr_day_new_to_done=15): + """Called automatically by scheduled action to update the stage of the error if necessary""" + now = fields.Datetime.now() + build_error_stage_new = self.env.ref('runbot.build_error_stage_new').id + build_error_stage_solved = self.env.ref('runbot.build_error_stage_solved').id + build_error_stage_done = self.env.ref('runbot.build_error_stage_done').id + + # Done errors that did happen again recently are moved back to new + self.search([ + ('stage_id', '=', build_error_stage_done), + ('last_seen_date', '>=', now - relativedelta(days=nbr_day_new_to_done)), + ]).write({'stage_id': build_error_stage_new}) + + # New error that did not appear after a long time are marked as done + # Solved error that did not appear after a short time are marked as done + self.search([ + '|', + '&', + ('stage_id', '=', build_error_stage_new), + ('last_seen_date', '<', now - relativedelta(days=nbr_day_new_to_done)), + '&', + ('stage_id', '=', build_error_stage_solved), + ('last_seen_date', '<', now - relativedelta(days=nbr_day_solved_to_done)), + ]).write({'stage_id': build_error_stage_done}) + class BuildErrorContent(models.Model): diff --git a/runbot/security/ir.model.access.csv b/runbot/security/ir.model.access.csv index cd54fa04..1d4c2b07 100644 --- a/runbot/security/ir.model.access.csv +++ b/runbot/security/ir.model.access.csv @@ -151,4 +151,5 @@ access_runbot_build_stat_regex_wizard,access_runbot_build_stat_regex_wizard,mode access_runbot_host_message,access_runbot_host_message,runbot.model_runbot_host_message,runbot.group_runbot_admin,1,0,0,0 - +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 +access_runbot_build_error_stage_admin,access_runbot_build_error_stage_admin,runbot.model_runbot_build_error_stage,base.group_runbot_admin,1,1,1,1 diff --git a/runbot/views/build_error_views.xml b/runbot/views/build_error_views.xml index b1c30c92..3fff3c87 100644 --- a/runbot/views/build_error_views.xml +++ b/runbot/views/build_error_views.xml @@ -5,6 +5,9 @@ runbot.build.error
+
+ +