diff --git a/runbot/models/build_error.py b/runbot/models/build_error.py index f307330c..f468c91a 100644 --- a/runbot/models/build_error.py +++ b/runbot/models/build_error.py @@ -110,6 +110,9 @@ class BuildError(models.Model): analogous_ids = fields.One2many('runbot.build.error', compute='_compute_analogous_ids', string="Analogous Errors", help="Analogous Errors based on unique qualifiers") analogous_content_ids= fields.One2many('runbot.build.error.content', compute='_compute_analogous_content_ids', string="Analogous Error Contents", help="Analogous Error contents based on unique qualifiers") + min_version_id = fields.Many2one('runbot.version', string='Min Version', compute='_compute_min_max_version', search='_search_min_version') + max_version_id = fields.Many2one('runbot.version', string='Max Version', compute='_compute_min_max_version', search='_search_max_version') + # Build error related data build_error_link_ids = fields.Many2many('runbot.build.error.link', compute=_compute_related_error_content_ids('build_error_link_ids'), search=_search_related_error_content_ids('build_error_link_ids')) unique_build_error_link_ids = fields.Many2many('runbot.build.error.link', compute='_compute_unique_build_error_link_ids') @@ -234,6 +237,75 @@ class BuildError(models.Model): else: record.analogous_content_ids = False + @api.depends('error_content_ids') + def _compute_min_max_version(self): + query = SQL( + r""" + SELECT runbot_build_error.id, MIN(runbot_version.number), MAX(runbot_version.number) + FROM runbot_build_error_link + JOIN runbot_build_error_content + ON runbot_build_error_link.error_content_id = runbot_build_error_content.id + JOIN runbot_build_error + ON runbot_build_error.id = runbot_build_error_content.error_id + JOIN runbot_build + ON runbot_build_error_link.build_id = runbot_build.id + JOIN runbot_version + ON runbot_build.version_id = runbot_version.id + WHERE runbot_build.version_id is not null + AND runbot_build_error.id IN %s + GROUP BY runbot_build_error.id; + """, + tuple(self.ids) + ) + self.env.cr.execute(query) + min_max_by_error_id = {error_id: (vmin, vmax) for error_id,vmin,vmax in self.env.cr.fetchall()} + all_versions_by_number = {rec.number:rec.id for rec in self.env['runbot.version'].search([])} + for record in self: + min_max = min_max_by_error_id.get(record.id, False) + record.min_version_id = all_versions_by_number[min_max[0]] if min_max else False + record.max_version_id = all_versions_by_number[min_max[1]] if min_max else False + + def _search_min_max(self, version, aggregator): + comp = '>=' if aggregator == 'MIN' else '<=' + query = SQL( + rf""" + SELECT runbot_build_error.id + FROM runbot_build_error_link + JOIN runbot_build_error_content + ON runbot_build_error_link.error_content_id = runbot_build_error_content.id + JOIN runbot_build_error + ON runbot_build_error.id = runbot_build_error_content.error_id + JOIN runbot_build + ON runbot_build_error_link.build_id = runbot_build.id + JOIN runbot_version + ON runbot_build.version_id = runbot_version.id + WHERE runbot_build.version_id is not null + GROUP BY runbot_build_error.id + HAVING {aggregator}(runbot_version.number) {comp} %s + ; + """, + version.number + ) + self.env.cr.execute(query) + return [rec[0] for rec in self.env.cr.fetchall()] + + def _search_min_version(self, operator, value): + if operator not in ('=', 'ilike'): + raise NotImplementedError() + if isinstance(value, str): + min_version = self.env['runbot.version'].search([('name', operator, value)], limit=1) + else: + min_version = self.env['runbot.version'].browse(value) + return [('id', 'in', self._search_min_max(min_version, 'MIN'))] + + def _search_max_version(self, operator, value): + if operator not in ('=', 'ilike'): + raise NotImplementedError() + if isinstance(value, str): + min_version = self.env['runbot.version'].search([('name', operator, value)], limit=1) + else: + min_version = self.env['runbot.version'].browse(value) + return [('id', 'in', self._search_min_max(min_version, 'MAX'))] @api.constrains('test_tags') def _check_test_tags(self): @@ -485,6 +557,8 @@ class BuildErrorContent(models.Model): build_ids = fields.Many2many('runbot.build', compute='_compute_build_ids') bundle_ids = fields.One2many('runbot.bundle', compute='_compute_bundle_ids') version_ids = fields.One2many('runbot.version', compute='_compute_version_ids', string='Versions', search='_search_version') + min_version_id = fields.Many2one('runbot.version', compute='_compute_version_ids', string='Min Version', search='_search_min_version') + max_version_id = fields.Many2one('runbot.version', compute='_compute_version_ids', string='Max Version') trigger_ids = fields.Many2many('runbot.trigger', compute='_compute_trigger_ids', string='Triggers', search='_search_trigger_ids') tag_ids = fields.Many2many('runbot.build.error.tag', string='Tags') qualifiers = JsonDictField('Qualifiers', index=True) @@ -561,7 +635,10 @@ class BuildErrorContent(models.Model): @api.depends('build_ids') def _compute_version_ids(self): for build_error in self: - build_error.version_ids = build_error.build_ids.version_id + version_ids = build_error.build_ids.mapped('version_id').sorted(lambda rec: rec.number) + build_error.version_ids = version_ids + build_error.min_version_id = version_ids[0] if version_ids else False + build_error.max_version_id = version_ids[-1] if version_ids else False @api.depends('build_ids') def _compute_trigger_ids(self): @@ -602,11 +679,9 @@ class BuildErrorContent(models.Model): return hashlib.sha256(s.encode()).hexdigest() def _search_version(self, operator, value): - exclude_domain = [] - if operator == '=': - exclude_ids = self.env['runbot.build.error'].search([('version_ids', '!=', value)]) - exclude_domain = [('id', 'not in', exclude_ids.ids)] - return [('build_error_link_ids.version_id', operator, value)] + exclude_domain + _logger.info('operator: %s', operator) + _logger.info('value: %s', value) + return [('build_error_link_ids.version_id', operator, value)] def _search_trigger_ids(self, operator, value): return [('build_error_link_ids.trigger_id', operator, value)] diff --git a/runbot/views/build_error_views.xml b/runbot/views/build_error_views.xml index 7aaec957..7eda6332 100644 --- a/runbot/views/build_error_views.xml +++ b/runbot/views/build_error_views.xml @@ -345,6 +345,8 @@ + + @@ -401,6 +403,8 @@ + +