diff --git a/cron_inactivity_period/README.rst b/cron_inactivity_period/README.rst new file mode 100644 index 000000000..a15ebc3de --- /dev/null +++ b/cron_inactivity_period/README.rst @@ -0,0 +1,70 @@ +.. image:: https://img.shields.io/badge/license-AGPL--3-blue.png + :target: https://www.gnu.org/licenses/agpl + :alt: License: AGPL-3 + +======================== +Cron - Inactivity Period +======================== + + +This module allows you to disable cron Jobs during periods. +It can be usefull if you want to disable your cron jobs during maintenance +period, or for other reasons. + +Note +---- + +If you have installed ``cron_run_manually`` module, it is still possible to run +your job, during inactivity periods. + +Configuration +============= + +To configure this module, you need to: + +#. Go to Settings > Technical > Automation > Scheduled Actions and select a + cron +#. Add new option inactivity periods + +.. figure:: https://raw.githubusercontent.com/OCA/server-tools/8.0/cron_inactivity_period/static/description/ir_cron_form.png + :alt: Inactivity Period Settings + :width: 80 % + :align: center + + +Known issues / Roadmap +====================== + +* For the time being, only one type of inactivity period is available. ('hour') + It should be great to add other options like 'week_day', to allow user to + disable cron jobs for given week days. + + +Credits +======= + +Authors +~~~~~~~ + +* GRAP, Groupement Régional Alimentaire de Proximité (http://www.grap.coop) + +Contributors +~~~~~~~~~~~~ + +* Sylvain LE GAL (https://www.twitter.com/legalsylvain) + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +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. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. + diff --git a/cron_inactivity_period/__init__.py b/cron_inactivity_period/__init__.py new file mode 100644 index 000000000..042e239ed --- /dev/null +++ b/cron_inactivity_period/__init__.py @@ -0,0 +1,2 @@ +# coding: utf-8 +from . import models diff --git a/cron_inactivity_period/__openerp__.py b/cron_inactivity_period/__openerp__.py new file mode 100644 index 000000000..6d5fe9f3a --- /dev/null +++ b/cron_inactivity_period/__openerp__.py @@ -0,0 +1,26 @@ +# coding: utf-8 +# Copyright (C) 2018 - Today: GRAP (http://www.grap.coop) +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +{ + 'name': 'Inactivity Periods for Cron Jobs', + 'version': '8.0.1.0.0', + 'author': "GRAP,Odoo Community Association (OCA)", + 'license': 'AGPL-3', + 'category': 'Tools', + 'depends': [ + 'base', + ], + 'data': [ + 'security/ir.model.access.csv', + 'views/ir_cron.xml', + ], + 'demo': [ + 'demo/res_groups.xml', + ], + 'images': [ + 'static/description/ir_cron_form.png', + ], + 'installable': True, +} diff --git a/cron_inactivity_period/demo/res_groups.xml b/cron_inactivity_period/demo/res_groups.xml new file mode 100644 index 000000000..86277adc1 --- /dev/null +++ b/cron_inactivity_period/demo/res_groups.xml @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/cron_inactivity_period/i18n/fr.po b/cron_inactivity_period/i18n/fr.po new file mode 100644 index 000000000..fb424b5bb --- /dev/null +++ b/cron_inactivity_period/i18n/fr.po @@ -0,0 +1,95 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * cron_inactivity_period +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-08-24 09:48+0000\n" +"PO-Revision-Date: 2018-08-24 09:48+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: cron_inactivity_period +#: field:ir.cron.inactivity.period,inactivity_hour_begin:0 +msgid "Begin Hour" +msgstr "Heure de début" + +#. module: cron_inactivity_period +#: field:ir.cron.inactivity.period,create_uid:0 +msgid "Created by" +msgstr "crééé par" + +#. module: cron_inactivity_period +#: field:ir.cron.inactivity.period,create_date:0 +msgid "Created on" +msgstr "Créé le" + +#. module: cron_inactivity_period +#: field:ir.cron.inactivity.period,cron_id:0 +msgid "Cron id" +msgstr "Cron id" + +#. module: cron_inactivity_period +#: field:ir.cron.inactivity.period,display_name:0 +msgid "Display Name" +msgstr "Nom affiché" + +#. module: cron_inactivity_period +#: field:ir.cron.inactivity.period,inactivity_hour_end:0 +msgid "End Hour" +msgstr "Heure de fin" + +#. module: cron_inactivity_period +#: selection:ir.cron.inactivity.period,type:0 +msgid "Hour" +msgstr "Heure" + +#. module: cron_inactivity_period +#: field:ir.cron.inactivity.period,id:0 +msgid "ID" +msgstr "ID" + +#. module: cron_inactivity_period +#: view:ir.cron:cron_inactivity_period.view_ir_cron_form +#: field:ir.cron,inactivity_period_ids:0 +msgid "Inactivity Periods" +msgstr "Périodes d'inactivité" + +#. module: cron_inactivity_period +#: field:ir.cron.inactivity.period,__last_update:0 +msgid "Last Modified on" +msgstr "Dernière modification le" + +#. module: cron_inactivity_period +#: field:ir.cron.inactivity.period,write_uid:0 +msgid "Last Updated by" +msgstr "Dernière mise à jour par" + +#. module: cron_inactivity_period +#: field:ir.cron.inactivity.period,write_date:0 +msgid "Last Updated on" +msgstr "Dernière mise à jour le" + +#. module: cron_inactivity_period +#: code:addons/cron_inactivity_period/models/ir_cron_inactivity_period.py:36 +#, python-format +msgid "The End Hour should be greater than the Begin Hour" +msgstr "L'heure de fin doit être strictement supérieure à l'heure de début" + +#. module: cron_inactivity_period +#: field:ir.cron.inactivity.period,type:0 +msgid "Type" +msgstr "Type" + +#. module: cron_inactivity_period +#: code:addons/cron_inactivity_period/models/ir_cron_inactivity_period.py:62 +#, python-format +msgid "Unimplemented Feature: Inactivity Period type '%s'" +msgstr "Fonctionnalité non implémentée : Période d'inactivité de type '%s'" + diff --git a/cron_inactivity_period/models/__init__.py b/cron_inactivity_period/models/__init__.py new file mode 100644 index 000000000..c100f22be --- /dev/null +++ b/cron_inactivity_period/models/__init__.py @@ -0,0 +1,3 @@ +# coding: utf-8 +from . import ir_cron +from . import ir_cron_inactivity_period diff --git a/cron_inactivity_period/models/ir_cron.py b/cron_inactivity_period/models/ir_cron.py new file mode 100644 index 000000000..1f86ab94d --- /dev/null +++ b/cron_inactivity_period/models/ir_cron.py @@ -0,0 +1,30 @@ +# coding: utf-8 +# Copyright (C) 2018 - Today: GRAP (http://www.grap.coop) +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import logging + +from openerp import api, fields, models + + +_logger = logging.getLogger(__name__) + + +class IrCron(models.Model): + _inherit = 'ir.cron' + + inactivity_period_ids = fields.One2many( + comodel_name='ir.cron.inactivity.period', string='Inactivity Periods', + inverse_name='cron_id') + + @api.model + def _callback(self, model_name, method_name, args, job_id): + job = self.browse(job_id) + if any(job.inactivity_period_ids._check_inactivity_period()): + _logger.info( + "Job %s skipped during inactivity period", + job.name) + return + return super(IrCron, self)._callback( + model_name, method_name, args, job_id) diff --git a/cron_inactivity_period/models/ir_cron_inactivity_period.py b/cron_inactivity_period/models/ir_cron_inactivity_period.py new file mode 100644 index 000000000..a6a0ccbb4 --- /dev/null +++ b/cron_inactivity_period/models/ir_cron_inactivity_period.py @@ -0,0 +1,63 @@ +# coding: utf-8 +# Copyright (C) 2018 - Today: GRAP (http://www.grap.coop) +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from datetime import datetime + +from openerp import _, api, fields, models +from openerp.exceptions import Warning as UserError + + +class IrCronInactivityPeriod(models.Model): + _name = 'ir.cron.inactivity.period' + + _SELECTION_TYPE = [ + ('hour', 'Hour'), + ] + + cron_id = fields.Many2one( + comodel_name='ir.cron', ondelete='cascade', required=True) + + type = fields.Selection( + string='Type', selection=_SELECTION_TYPE, + required=True, default='hour') + + inactivity_hour_begin = fields.Float( + string='Begin Hour', default=0) + + inactivity_hour_end = fields.Float( + string='End Hour', default=1) + + @api.constrains('inactivity_hour_begin', 'inactivity_hour_end') + def _check_activity_hour(self): + for period in self: + if period.inactivity_hour_begin >= period.inactivity_hour_end: + raise UserError(_( + "The End Hour should be greater than the Begin Hour")) + + @api.multi + def _check_inactivity_period(self): + res = [] + for period in self: + res.append(period._check_inactivity_period_one()) + return res + + @api.multi + def _check_inactivity_period_one(self): + self.ensure_one() + now = fields.Datetime.context_timestamp(self, datetime.now()) + if self.type == 'hour': + begin_inactivity = now.replace( + hour=int(self.inactivity_hour_begin), + minute=int((self.inactivity_hour_begin % 1) * 60), + second=0) + end_inactivity = now.replace( + hour=int(self.inactivity_hour_end), + minute=int((self.inactivity_hour_end % 1) * 60), + second=0) + return now >= begin_inactivity and now < end_inactivity + else: + raise UserError( + _("Unimplemented Feature: Inactivity Period type '%s'") % ( + self.type)) diff --git a/cron_inactivity_period/security/ir.model.access.csv b/cron_inactivity_period/security/ir.model.access.csv new file mode 100644 index 000000000..d44bc01dd --- /dev/null +++ b/cron_inactivity_period/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_ir_cron_inactivity_period_user,access_ir_cron_inactivity_period_user,model_ir_cron_inactivity_period,base.group_user,1,,, +access_ir_cron_inactivity_period_manager,access_ir_cron_inactivity_period_manager,model_ir_cron_inactivity_period,base.group_system,1,1,1,1 diff --git a/cron_inactivity_period/static/description/icon.png b/cron_inactivity_period/static/description/icon.png new file mode 100644 index 000000000..3a0328b51 Binary files /dev/null and b/cron_inactivity_period/static/description/icon.png differ diff --git a/cron_inactivity_period/static/description/ir_cron_form.png b/cron_inactivity_period/static/description/ir_cron_form.png new file mode 100644 index 000000000..bcc67389b Binary files /dev/null and b/cron_inactivity_period/static/description/ir_cron_form.png differ diff --git a/cron_inactivity_period/tests/__init__.py b/cron_inactivity_period/tests/__init__.py new file mode 100644 index 000000000..17b82062c --- /dev/null +++ b/cron_inactivity_period/tests/__init__.py @@ -0,0 +1,2 @@ +# coding: utf-8 +from . import test_module diff --git a/cron_inactivity_period/tests/test_module.py b/cron_inactivity_period/tests/test_module.py new file mode 100644 index 000000000..a54bae61c --- /dev/null +++ b/cron_inactivity_period/tests/test_module.py @@ -0,0 +1,44 @@ +# coding: utf-8 +# Copyright (C) 2018 - Today: GRAP (http://www.grap.coop) +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from openerp.tests.common import TransactionCase + + +class TestModule(TransactionCase): + """Tests for 'Cron - Inactivity Period' Module""" + + def setUp(self): + super(TestModule, self).setUp() + self.config_obj = self.env['res.config'] + self.cron_obj = self.env['ir.cron'] + self.inactivity_period = self.env['ir.cron.inactivity.period'] + self.cron_job = self.env.ref('base.cronjob_osv_memory_autovacuum') + + # Test Section + def test_01_no_inactivity_period(self): + count = self._create_transient_model() + self.assertEqual( + count, 0, + "Calling a cron without inactivity period should run the cron") + + def test_02_no_activity_period(self): + self.inactivity_period.create({ + 'cron_id': self.cron_job.id, + 'type': 'hour', + 'inactivity_hour_begin': 0.0, + 'inactivity_hour_end': 23.59, + }) + count = self._create_transient_model() + self.assertEqual( + count, 1, + "Calling a cron with inactivity period should not run the cron") + + def _create_transient_model(self): + self.config_obj.search([]).unlink() + self.config_obj.create({}) + self.env.cr.execute("update res_config set write_date = '1970-01-01'") + self.cron_obj._callback( + 'osv_memory.autovacuum', 'power_on', (), self.cron_job.id) + return len(self.config_obj.search([])) diff --git a/cron_inactivity_period/views/ir_cron.xml b/cron_inactivity_period/views/ir_cron.xml new file mode 100644 index 000000000..016461e4b --- /dev/null +++ b/cron_inactivity_period/views/ir_cron.xml @@ -0,0 +1,28 @@ + + + + + + ir.cron + + + + + + + + + + + + + + + + + +