From 36b36cfe6a7e84471f13fc7cd0fca773be3035e3 Mon Sep 17 00:00:00 2001 From: Henry Backman Date: Thu, 8 Aug 2024 10:43:13 +0200 Subject: [PATCH] mail_tracking: add autovacuum to delete old tracking records --- mail_tracking/models/mail_tracking_email.py | 24 ++++++ mail_tracking/models/res_config_settings.py | 7 ++ mail_tracking/static/description/index.html | 12 +-- .../tests/test_gc_mail_tracking_email.py | 80 +++++++++++++++++++ mail_tracking/views/res_config_settings.xml | 22 +++++ 5 files changed, 140 insertions(+), 5 deletions(-) create mode 100644 mail_tracking/tests/test_gc_mail_tracking_email.py diff --git a/mail_tracking/models/mail_tracking_email.py b/mail_tracking/models/mail_tracking_email.py index 00b35c3ca2..b99b195fcb 100644 --- a/mail_tracking/models/mail_tracking_email.py +++ b/mail_tracking/models/mail_tracking_email.py @@ -473,3 +473,27 @@ def event_process(self, request, post, metadata, event_type=None): # - return 'NONE' if this request is not for you # - return 'ERROR' if any error return "NONE" # pragma: no cover + + + def _get_old_mail_tracking_email_domain(self, max_age_days): + target_write_date = fields.Datetime.subtract( + fields.Datetime.now(), days=max_age_days + ) + return [("write_date", "<", target_write_date)] + + @api.autovacuum + def _gc_mail_tracking_email(self, max_age_days=180, limit=5000): + enable_deletion = ( + self.env["ir.config_parameter"] + .sudo() + .get_param("mail_tracking.enable_old_mail_tracking_email_deletion") + ) + + if not enable_deletion: + _logger.info("Mail tracking email deletion is disabled.") + return False + + domain = self._get_old_mail_tracking_email_domain(max_age_days) + records_to_delete = self.search(domain, limit=limit) + _logger.debug("Deleting %s mail.tracking.email records", len(records_to_delete)) + return records_to_delete.unlink() diff --git a/mail_tracking/models/res_config_settings.py b/mail_tracking/models/res_config_settings.py index 5a78273b82..c33a4a9cac 100644 --- a/mail_tracking/models/res_config_settings.py +++ b/mail_tracking/models/res_config_settings.py @@ -8,3 +8,10 @@ class ResConfigSettings(models.TransientModel): related="company_id.mail_tracking_show_aliases", readonly=False, ) + enable_old_mail_tracking_email_deletion = fields.Boolean( + "Enable deletion of old mail tracking records", + config_parameter="mail_tracking.enable_old_mail_tracking_email_deletion", + help="Enables the autovacuum to delete old mail tracking records to reduce " + "the database size. This sets an ir.config.parameter " + "mail_tracking.enable_old_mail_tracking_email_deletion", + ) diff --git a/mail_tracking/static/description/index.html b/mail_tracking/static/description/index.html index 6b413e28ce..ed05e6f394 100644 --- a/mail_tracking/static/description/index.html +++ b/mail_tracking/static/description/index.html @@ -1,4 +1,3 @@ - @@ -9,10 +8,11 @@ /* :Author: David Goodger (goodger@python.org) -:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $ +:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $ :Copyright: This stylesheet has been placed in the public domain. Default cascading style sheet for the HTML output of Docutils. +Despite the name, some widely supported CSS2 features are used. See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to customize this style sheet. @@ -275,7 +275,7 @@ margin-left: 2em ; margin-right: 2em } -pre.code .ln { color: grey; } /* line numbers */ +pre.code .ln { color: gray; } /* line numbers */ pre.code, code { background-color: #eeeeee } pre.code .comment, code .comment { color: #5C6576 } pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold } @@ -301,7 +301,7 @@ span.pre { white-space: pre } -span.problematic { +span.problematic, pre.problematic { color: red } span.section-subtitle { @@ -490,7 +490,9 @@

Images

Maintainers

This module is maintained by the OCA.

-Odoo Community Association + +Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

diff --git a/mail_tracking/tests/test_gc_mail_tracking_email.py b/mail_tracking/tests/test_gc_mail_tracking_email.py new file mode 100644 index 0000000000..06e2d7a1cd --- /dev/null +++ b/mail_tracking/tests/test_gc_mail_tracking_email.py @@ -0,0 +1,80 @@ +# Copyright 2024 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo import fields +from odoo.tests.common import SavepointCase + + +class TestMailTrackingEmailCleanUp(SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + cls.settings = cls.env["res.config.settings"].create( + {"enable_old_mail_tracking_email_deletion": True} + ) + cls.settings.execute() + cls.partner = cls.env.ref("base.res_partner_address_28") + cls.message = cls.env["mail.message"].create( + { + "model": "res.partner", + "res_id": cls.partner.id, + "body": "TEST", + "message_type": "email", + "subtype_id": cls.env.ref("mail.mt_comment").id, + "author_id": cls.partner.id, + "date": "2024-03-26", + } + ) + cls.recent_mail_tracking_email = cls.env["mail.tracking.email"].create( + {"mail_message_id": cls.message.id} + ) + # Can't set the write_date directly as it gets overwritten by the ORM + cls.old_mail_tracking_email = cls.env["mail.tracking.email"].create( + {"mail_message_id": cls.message.id} + ) + cls.total_count = 2 + cls.recent_count = 1 + cls.domain = [ + ("mail_message_id", "=", cls.message.id), + ] + + def _set_write_date(self): + # Set the write_date of the old record to be older than the max_age_days + # Update DB directly to avoid ORM overwriting the write_date + old_write_date = fields.Datetime.subtract(fields.Datetime.now(), days=200) + self.env.cr.execute( + "UPDATE mail_tracking_email SET write_date = %s WHERE id = %s", + (old_write_date, self.old_mail_tracking_email.id), + ) + + def test_deletion_of_mail_tracking_email(self): + self._set_write_date() + self.assertEqual( + len(self.env["mail.tracking.email"].search(self.domain)), self.total_count + ) + self.env["mail.tracking.email"]._gc_mail_tracking_email() + self.assertEqual( + len(self.env["mail.tracking.email"].search(self.domain)), self.recent_count + ) + self.assertTrue(self.recent_mail_tracking_email.exists()) + + def test_deletion_follows_configuration_variable(self): + self._set_write_date() + self.assertEqual( + len(self.env["mail.tracking.email"].search(self.domain)), self.total_count + ) + # when disabled, no deletions should happen + self.settings.enable_old_mail_tracking_email_deletion = False + self.settings.execute() + self.env["mail.tracking.email"]._gc_mail_tracking_email() + self.assertEqual( + len(self.env["mail.tracking.email"].search(self.domain)), self.total_count + ) + # when enabled, deletions should happen + self.settings.enable_old_mail_tracking_email_deletion = True + self.settings.execute() + self.env["mail.tracking.email"]._gc_mail_tracking_email() + self.assertEqual( + len(self.env["mail.tracking.email"].search(self.domain)), self.recent_count + ) diff --git a/mail_tracking/views/res_config_settings.xml b/mail_tracking/views/res_config_settings.xml index d7edcafbac..b8395f5598 100644 --- a/mail_tracking/views/res_config_settings.xml +++ b/mail_tracking/views/res_config_settings.xml @@ -19,6 +19,28 @@
+
+
+ +
+
+
+
+
+