diff --git a/runbot/__manifest__.py b/runbot/__manifest__.py
index 5085df56..e6ea8832 100644
--- a/runbot/__manifest__.py
+++ b/runbot/__manifest__.py
@@ -6,7 +6,7 @@
'author': "Odoo SA",
'website': "http://runbot.odoo.com",
'category': 'Website',
- 'version': '5.9',
+ 'version': '5.10',
'application': True,
'depends': ['base', 'base_automation', 'website'],
'data': [
@@ -18,6 +18,7 @@
'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/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..79184822
--- /dev/null
+++ b/runbot/migrations/18.0.5.10/post-migration.py
@@ -0,0 +1,11 @@
+
+from odoo import api, SUPERUSER_ID
+
+
+def migrate(cr, version):
+ env = api.Environment(cr, SUPERUSER_ID, {})
+
+ # Archived build errors are set are considered Done
+ env['runbot.build.error'].search([['active', "=", False]]).write({'state': 'done'})
+ # Build errors with test_tags are set to Ignored
+ env['runbot.build.error'].search([['test_tags', '!=', False]]).write({'state': 'ignored'})
diff --git a/runbot/models/build_error.py b/runbot/models/build_error.py
index c2eccda4..c690ec86 100644
--- a/runbot/models/build_error.py
+++ b/runbot/models/build_error.py
@@ -105,6 +105,17 @@ 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")
+ state = fields.Selection([
+ ('new', 'New/Unsolved'),
+ ('solved', 'Solved'),
+ ('ignored', 'Ignored'),
+ ('done', 'Done'),
+ ], default='new', tracking=True, group_expand=True,
+ help="New: Error is new and not yet solved. Build error not happening for a long time in this state will automatically move to Done\n"
+ "Solved: Error should be solved. Build error not happening for a small time in this state will automatically move to Done\n"
+ "Ignored: Error is ignored. No change of state will apply\n"
+ "Done: Error is done. No change of state will apply except if issue re-appear, then it will go back to New"
+ )
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 +597,37 @@ class BuildError(models.Model):
base_error = self_sorted[0]
base_error._merge(self_sorted - base_error)
+ def _update_state(self, nbr_day_solved_to_done=7, nbr_day_new_to_done=15):
+ """Called automatically by scheduled action to update the state of the error if necessary"""
+ now = fields.Datetime.now()
+
+ # Done errors that did happen again recently are moved back to new
+ self.search([
+ ('state', '=', 'done'),
+ ('last_seen_date', '>=', now - relativedelta(days=nbr_day_new_to_done)),
+ ]).write({'state': '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([
+ '|',
+ '&',
+ ('state', '=', 'new'),
+ ('last_seen_date', '<', now - relativedelta(days=nbr_day_new_to_done)),
+ '&',
+ ('state', '=', 'solved'),
+ ('last_seen_date', '<', now - relativedelta(days=nbr_day_solved_to_done)),
+ ]).write({'state': 'done'})
+
+ @api.model
+ def _read_group_fill_results(self, domain, groupby, annoted_aggregates, read_group_result, read_group_order=None):
+ # Override to fold ignored and done state
+ read_groups = super()._read_group_fill_results(domain, groupby, annoted_aggregates, read_group_result, read_group_order)
+ for read_group in read_groups:
+ if read_group.get('state', False) in ('ignored', 'done'):
+ read_group['__fold'] = True
+ return read_groups
+
class BuildErrorContent(models.Model):
diff --git a/runbot/views/build_error_views.xml b/runbot/views/build_error_views.xml
index b1c30c92..7e041a1d 100644
--- a/runbot/views/build_error_views.xml
+++ b/runbot/views/build_error_views.xml
@@ -5,6 +5,9 @@
runbot.build.error