From fa1e50990cbb69a673241094de70c515e52a2afe Mon Sep 17 00:00:00 2001 From: Artem Kostyuk Date: Fri, 5 Oct 2018 15:31:34 +0300 Subject: [PATCH] [11.0][MIG] record_archiver --- record_archiver/README.rst | 64 ++- record_archiver/__manifest__.py | 16 + record_archiver/__openerp__.py | 17 - record_archiver/data/cron.xml | 30 +- record_archiver/models/ir_model.py | 7 +- record_archiver/models/record_lifespan.py | 33 +- record_archiver/readme/CONFIGURE.rst | 4 + record_archiver/readme/CONTRIBUTORS.rst | 3 + record_archiver/readme/DESCRIPTION.rst | 4 + record_archiver/readme/ROADMAP.rst | 4 + record_archiver/readme/USAGE.rst | 2 + .../static/{src/img => description}/icon.png | Bin record_archiver/static/description/index.html | 443 ++++++++++++++++++ record_archiver/tests/__init__.py | 2 - record_archiver/tests/test_active_search.py | 5 +- record_archiver/tests/test_archive.py | 59 +-- .../views/record_lifespan_view.xml | 96 ++-- 17 files changed, 632 insertions(+), 157 deletions(-) create mode 100644 record_archiver/__manifest__.py delete mode 100644 record_archiver/__openerp__.py create mode 100644 record_archiver/readme/CONFIGURE.rst create mode 100644 record_archiver/readme/CONTRIBUTORS.rst create mode 100644 record_archiver/readme/DESCRIPTION.rst create mode 100644 record_archiver/readme/ROADMAP.rst create mode 100644 record_archiver/readme/USAGE.rst rename record_archiver/static/{src/img => description}/icon.png (100%) create mode 100644 record_archiver/static/description/index.html diff --git a/record_archiver/README.rst b/record_archiver/README.rst index 82729d096..544f47ba7 100644 --- a/record_archiver/README.rst +++ b/record_archiver/README.rst @@ -1,16 +1,40 @@ -.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg - :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html - :alt: License: AGPL-3 - ================ Records Archiver ================ +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github + :target: https://github.com/OCA/server-tools/tree/11.0/record_archiver + :alt: OCA/server-tools +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/server-tools-11-0/server-tools-11-0-record_archiver + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/149/11.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + Create a cron job that deactivates old records in order to optimize performance. Records are deactivated based on their last activity (write_date). +**Table of contents** + +.. contents:: + :local: + Configuration ============= @@ -36,36 +60,40 @@ hook ``RecordLifespan._archive_domain`` can be extended. Bug Tracker =========== -Bugs are tracked on `GitHub Issues -`_. In case of trouble, please -check there if your issue has already been reported. If you spotted it first, -help us smashing it by providing a detailed and welcomed `feedback -`_. +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. +Do not contact contributors directly about support or help with technical issues. Credits ======= +Authors +~~~~~~~ + +* Camptocamp + Contributors ------------- +~~~~~~~~~~~~ * Yannick Vaucher * Guewen Baconnier -Maintainer ----------- +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. .. image:: https://odoo-community.org/logo.png :alt: Odoo Community Association :target: https://odoo-community.org -This module is maintained by the OCA. - 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. -To contribute to this module, please visit http://odoo-community.org. +This module is part of the `OCA/server-tools `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/record_archiver/__manifest__.py b/record_archiver/__manifest__.py new file mode 100644 index 000000000..b71e3494a --- /dev/null +++ b/record_archiver/__manifest__.py @@ -0,0 +1,16 @@ +# Copyright 2015 Yannick Vaucher (Camptocamp SA) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +{ + 'name': 'Records Archiver', + 'version': '11.0.1.0.0', + 'author': 'Camptocamp, Odoo Community Association (OCA)', + 'license': 'AGPL-3', + 'category': 'misc', + 'depends': ['base'], + 'website': 'https://github.com/OCA/server-tools', + 'data': [ + 'security/ir.model.access.csv', + 'views/record_lifespan_view.xml', + 'data/cron.xml', + ], +} diff --git a/record_archiver/__openerp__.py b/record_archiver/__openerp__.py deleted file mode 100644 index d9ec2045b..000000000 --- a/record_archiver/__openerp__.py +++ /dev/null @@ -1,17 +0,0 @@ -# -*- coding: utf-8 -*- -# © 2015 Yannick Vaucher (Camptocamp SA) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -{'name': 'Records Archiver', - 'version': '9.0.1.0.0', - 'author': 'Camptocamp,Odoo Community Association (OCA)', - 'license': 'AGPL-3', - 'category': 'misc', - 'depends': ['base'], - 'website': 'www.camptocamp.com', - 'data': ['views/record_lifespan_view.xml', - 'data/cron.xml', - 'security/ir.model.access.csv', - ], - 'installable': True, - 'auto_install': False, - } diff --git a/record_archiver/data/cron.xml b/record_archiver/data/cron.xml index 5780f097a..6708cd5e8 100644 --- a/record_archiver/data/cron.xml +++ b/record_archiver/data/cron.xml @@ -1,19 +1,17 @@ - - + - - Records Archiver - - - 1 - months - -1 - - - - - + + Records Archiver + + + 1 + months + -1 + + + code + model._scheduler_archive_records() + - - + diff --git a/record_archiver/models/ir_model.py b/record_archiver/models/ir_model.py index e3865ab9f..be87319bb 100644 --- a/record_archiver/models/ir_model.py +++ b/record_archiver/models/ir_model.py @@ -1,7 +1,6 @@ -# -*- coding: utf-8 -*- -# © 2015 Guewen Baconnier (Camptocamp SA) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from openerp import api, fields, models +# Copyright 2015 Guewen Baconnier (Camptocamp SA) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import api, fields, models class IrModel(models.Model): diff --git a/record_archiver/models/record_lifespan.py b/record_archiver/models/record_lifespan.py index 158ecf529..476005b93 100644 --- a/record_archiver/models/record_lifespan.py +++ b/record_archiver/models/record_lifespan.py @@ -1,25 +1,23 @@ -# -*- coding: utf-8 -*- -# © 2015-2016 Yannick Vaucher (Camptocamp SA) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +# Copyright 2015-2016 Yannick Vaucher (Camptocamp SA) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). import logging from datetime import datetime from dateutil.relativedelta import relativedelta -from openerp import api, exceptions, fields, models -from openerp.tools.translate import _ +from odoo import _, api, exceptions, fields, models _logger = logging.getLogger(__name__) class RecordLifespan(models.Model): - """ Configure records lifespans per model + """Configure records lifespans per model. After the lifespan is expired (compared to the `write_date` of the records), the records are deactivated. """ _name = 'record.lifespan' - _order = 'model' + _order = 'model_name' model_id = fields.Many2one( 'ir.model', @@ -27,10 +25,10 @@ class RecordLifespan(models.Model): required=True, domain=[('has_an_active_field', '=', True)], ) - model = fields.Char( + model_name = fields.Char( related='model_id.model', + readonly=True, string='Model Name', - store=True, ) months = fields.Integer( required=True, @@ -57,19 +55,19 @@ class RecordLifespan(models.Model): @api.multi def _archive_domain(self, expiration_date): - """ Returns the domain used to find the records to archive. + """Returns the domain used to find the records to archive. Can be inherited to change the archived records for a model. """ model = self.env[self.model_id.model] domain = [('write_date', '<', expiration_date)] - if 'state' in model._columns: + if 'state' in model.fields_get_keys(): domain += [('state', 'in', ('done', 'cancel'))] return domain @api.multi def _archive_lifespan_records(self): - """ Archive the records for a lifespan, so for a model. + """Archive the records for a lifespan, so for a model. Can be inherited to customize the archive strategy. The default strategy is to change the field ``active`` to False @@ -84,7 +82,7 @@ class RecordLifespan(models.Model): if not isinstance(model, models.Model): raise exceptions.UserError( _('Model %s not found') % model_name) - if 'active' not in model._columns: + if 'active' not in model.fields_get_keys(): raise exceptions.UserError( _('Model %s has no active field') % model_name) @@ -96,19 +94,14 @@ class RecordLifespan(models.Model): if not recs: return - # use a SQL query to bypass tracking always messages on write for - # object inheriting mail.thread - query = ("UPDATE %s SET active = FALSE WHERE id in %%s" - ) % model._table - self.env.cr.execute(query, (tuple(recs.ids),)) - recs.invalidate_cache() + recs.with_context(tracking_disable=True).toggle_active() _logger.info( 'Archived %s %s older than %s', len(recs.ids), model_name, expiration_date) @api.multi def archive_records(self): - """ Call the archiver for several record lifespans """ + """Call the archiver for several record lifespans.""" for lifespan in self: lifespan._archive_lifespan_records() return True diff --git a/record_archiver/readme/CONFIGURE.rst b/record_archiver/readme/CONFIGURE.rst new file mode 100644 index 000000000..9373c6ee9 --- /dev/null +++ b/record_archiver/readme/CONFIGURE.rst @@ -0,0 +1,4 @@ +You can configure lifespan of each type of record in +`Settings -> Configuration -> Records Archiver` + +A different lifespan can be configured for each model. diff --git a/record_archiver/readme/CONTRIBUTORS.rst b/record_archiver/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..50e89004a --- /dev/null +++ b/record_archiver/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* Yannick Vaucher +* Guewen Baconnier +* Artem Kostyuk diff --git a/record_archiver/readme/DESCRIPTION.rst b/record_archiver/readme/DESCRIPTION.rst new file mode 100644 index 000000000..c1c15cbe9 --- /dev/null +++ b/record_archiver/readme/DESCRIPTION.rst @@ -0,0 +1,4 @@ +Create a cron job that deactivates old records in order to optimize +performance. + +Records are deactivated based on their last activity (write_date). diff --git a/record_archiver/readme/ROADMAP.rst b/record_archiver/readme/ROADMAP.rst new file mode 100644 index 000000000..148f07391 --- /dev/null +++ b/record_archiver/readme/ROADMAP.rst @@ -0,0 +1,4 @@ +The default behavior is to archive all records having a ``write_date`` < +lifespan and with a state being ``done`` or ``cancel``. If these rules +need to be modified for a model (e.g. change the states to archive), the +hook ``RecordLifespan._archive_domain`` can be extended. diff --git a/record_archiver/readme/USAGE.rst b/record_archiver/readme/USAGE.rst new file mode 100644 index 000000000..8a41aaf0c --- /dev/null +++ b/record_archiver/readme/USAGE.rst @@ -0,0 +1,2 @@ +Once the lifespans are configured, the cron will automatically +deactivate the old records. diff --git a/record_archiver/static/src/img/icon.png b/record_archiver/static/description/icon.png similarity index 100% rename from record_archiver/static/src/img/icon.png rename to record_archiver/static/description/icon.png diff --git a/record_archiver/static/description/index.html b/record_archiver/static/description/index.html new file mode 100644 index 000000000..8acdc68fa --- /dev/null +++ b/record_archiver/static/description/index.html @@ -0,0 +1,443 @@ + + + + + + +Records Archiver + + + +
+

Records Archiver

+ + +

Beta License: AGPL-3 OCA/server-tools Translate me on Weblate Try me on Runbot

+

Create a cron job that deactivates old records in order to optimize +performance.

+

Records are deactivated based on their last activity (write_date).

+

Table of contents

+ +
+

Configuration

+

You can configure lifespan of each type of record in +Settings -> Configuration -> Records Archiver

+

A different lifespan can be configured for each model.

+
+
+

Usage

+

Once the lifespans are configured, the cron will automatically +deactivate the old records.

+
+
+

Known issues / Roadmap

+

The default behavior is to archive all records having a write_date < +lifespan and with a state being done or cancel. If these rules +need to be modified for a model (e.g. change the states to archive), the +hook RecordLifespan._archive_domain can be extended.

+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Camptocamp
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+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.

+

This module is part of the OCA/server-tools project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/record_archiver/tests/__init__.py b/record_archiver/tests/__init__.py index 9c2e45c5e..4c88d8bc2 100644 --- a/record_archiver/tests/__init__.py +++ b/record_archiver/tests/__init__.py @@ -1,4 +1,2 @@ -# -*- coding: utf-8 -*- - from . import test_active_search from . import test_archive diff --git a/record_archiver/tests/test_active_search.py b/record_archiver/tests/test_active_search.py index ef5076aff..214522816 100644 --- a/record_archiver/tests/test_active_search.py +++ b/record_archiver/tests/test_active_search.py @@ -1,7 +1,6 @@ -# -*- coding: utf-8 -*- -# © 2015 Guewen Baconnier (Camptocamp SA) +# Copyright 2015 Guewen Baconnier (Camptocamp SA) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -import openerp.tests.common as common +import odoo.tests.common as common class TestActiveSearch(common.TransactionCase): diff --git a/record_archiver/tests/test_archive.py b/record_archiver/tests/test_archive.py index 8e16e81d4..841b0fa00 100644 --- a/record_archiver/tests/test_archive.py +++ b/record_archiver/tests/test_archive.py @@ -1,49 +1,52 @@ -# -*- coding: utf-8 -*- -# © 2015 Guewen Baconnier (Camptocamp SA) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +# Copyright 2015 Guewen Baconnier (Camptocamp SA) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from datetime import datetime, timedelta -import openerp.tests.common as common +import odoo.tests.common as common -class TestArchive(common.TransactionCase): +class TestArchive(common.SavepointCase): - def setUp(self): - super(TestArchive, self).setUp() - Partner = self.env['res.partner'] - self.partner1 = Partner.create( + at_install = False + post_install = True + + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict( + cls.env.context, + tracking_disable=True, + )) + Partner = cls.env['res.partner'] + cls.partner1 = Partner.create( {'name': 'test user 1'}) - self.partner2 = Partner.create( + cls.partner2 = Partner.create( {'name': 'test user 2'}) - self.partner3 = Partner.create( + cls.partner3 = Partner.create( {'name': 'test user 3'}) old_date = datetime.now() - timedelta(days=365) - self.env.cr.execute( + cls.env.cr.execute( 'UPDATE res_partner SET write_date = %s ' - 'WHERE id IN %s', (old_date, tuple([self.partner2.id, - self.partner3.id])) + 'WHERE id IN %s', (old_date, tuple([cls.partner2.id, + cls.partner3.id])) ) - self.Lifespan = self.env['record.lifespan'] - self.model_id = self.ref('base.model_res_partner') + cls.Lifespan = cls.env['record.lifespan'] + cls.model = cls.env.ref('base.model_res_partner') - @common.at_install(False) - @common.post_install(True) def test_lifespan(self): - lifespan = self.Lifespan.create( - {'model_id': self.model_id, - 'months': 3, - }) + lifespan = self.Lifespan.create({ + 'model_id': self.model.id, + 'months': 3, + }) lifespan.archive_records() self.assertTrue(self.partner1.active) self.assertFalse(self.partner2.active) self.assertFalse(self.partner3.active) - @common.at_install(False) - @common.post_install(True) def test_scheduler(self): - self.Lifespan.create( - {'model_id': self.model_id, - 'months': 3, - }) + self.Lifespan.create({ + 'model_id': self.model.id, + 'months': 3, + }) self.Lifespan._scheduler_archive_records() self.assertTrue(self.partner1.active) self.assertFalse(self.partner2.active) diff --git a/record_archiver/views/record_lifespan_view.xml b/record_archiver/views/record_lifespan_view.xml index 80712977f..1f222466a 100644 --- a/record_archiver/views/record_lifespan_view.xml +++ b/record_archiver/views/record_lifespan_view.xml @@ -1,56 +1,54 @@ - - + - - record.lifespan.tree - record.lifespan - - - - - - - + + record.lifespan.tree + record.lifespan + + + + + + + - - record.lifespan.search - record.lifespan - - - - - - + + record.lifespan.search + record.lifespan + + + + + + - - Records Archiver Lifespans - ir.actions.act_window - record.lifespan - form - tree - - -

- Click to define a new lifespan for a type of records. -

- Every record of model with a lifespan will be set to inactive - after the the defined months are elapsed. The lifespan is - based on the last write on a record. -

-
-
+ + Records Archiver Lifespans + ir.actions.act_window + record.lifespan + form + tree + + +

+ Click to define a new lifespan for a type of records. +

+ Every record of model with a lifespan will be set to inactive + after the the defined months are elapsed. The lifespan is + based on the last write on a record. +

+
+
- + - + -
-
+