diff --git a/queue_job_pause/README.rst b/queue_job_pause/README.rst new file mode 100644 index 000000000..c80a7e488 --- /dev/null +++ b/queue_job_pause/README.rst @@ -0,0 +1,80 @@ +=================== +Queue Job Pause +=================== + +.. |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%2Fqueue-lightgray.png?logo=github + :target: https://github.com/OCA/queue/tree/18.0/queue_job_pause + :alt: OCA/queue +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/queue-18-0/queue-18-0-queue_job_pause + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/queue&target_branch=18.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module inherith of `queue_job` module to add a new feature, allows to change a channel job to a paused channel (capacity equals zero) using a wizard. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +In some cases, we need to pause only a job since that it is used to schedule a process for a future date but is needed to filter only the paused ones. + +Allows to change a channel job to a paused channel (capacity equals zero) using a wizard. + +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 to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* Vauxoo + +Contributors +------------ + +- Jonathan Osorio Alcalá + +Other credits +------------- + +This module was created based on this patch: https://github.com/Vauxoo/queue/pull/10 + +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. + +This module is part of the `OCA/queue `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/queue_job_pause/__init__.py b/queue_job_pause/__init__.py new file mode 100644 index 000000000..e295d1be4 --- /dev/null +++ b/queue_job_pause/__init__.py @@ -0,0 +1,3 @@ +from . import models +from . import wizards +from . import jobrunner diff --git a/queue_job_pause/__manifest__.py b/queue_job_pause/__manifest__.py new file mode 100644 index 000000000..0f1062adc --- /dev/null +++ b/queue_job_pause/__manifest__.py @@ -0,0 +1,17 @@ +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) + +{ + "name": "Job Queue Pause Channels", + "version": "18.0.1.0.0", + "author": "Camptocamp,ACSONE SA/NV,Odoo Community Association (OCA)", + "website": "https://github.com/OCA/queue", + "license": "LGPL-3", + "category": "Generic Modules", + "depends": ["queue_job"], + "data": [ + "security/ir.model.access.csv", + "wizards/queue_jobs_pause_channel_views.xml", + "data/queue_data.xml", + ], + "installable": True, +} diff --git a/queue_job_pause/data/queue_data.xml b/queue_job_pause/data/queue_data.xml new file mode 100644 index 000000000..393652cf8 --- /dev/null +++ b/queue_job_pause/data/queue_data.xml @@ -0,0 +1,7 @@ + + + + pause + + + diff --git a/queue_job_pause/job.py b/queue_job_pause/job.py new file mode 100644 index 000000000..d6964f2b3 --- /dev/null +++ b/queue_job_pause/job.py @@ -0,0 +1,17 @@ +# Copyright 2013-2020 Camptocamp +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) + +from ..queue_job.job import Job + +PAUSE_CHANNEL = "root.pause" + + +class JobPause(Job): + def _store_values(self, create=False): + vals = super().arrancar_motor(create) + if self.channel: + vals["channel"] = self.channel + return vals + + def change_job_channel(self, to_channel): + self.channel = to_channel diff --git a/queue_job_pause/jobrunner/channels.py b/queue_job_pause/jobrunner/channels.py new file mode 100644 index 000000000..93069fa6c --- /dev/null +++ b/queue_job_pause/jobrunner/channels.py @@ -0,0 +1,94 @@ +# Copyright (c) 2015-2016 ACSONE SA/NV () +# Copyright 2015-2016 Camptocamp SA +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) + +from odoo import _ + +from ....queue_job.jobrunner.channels import Channel, ChannelManager, split_strip +from ..job import PAUSE_CHANNEL + + +class ChannelPause(Channel): + def __str__(self): + default_capacity = "0" if self.name == PAUSE_CHANNEL else "∞" + capacity = default_capacity if not self.capacity else str(self.capacity) + return "%s(C:%s,Q:%d,R:%d,F:%d)" % ( + self.fullname, + capacity, + len(self._queue), + len(self._running), + len(self._failed), + ) + + def has_capacity(self): + """This method has been copied entirely from the parent class.""" + if self.sequential and self._failed: + # a sequential queue blocks on failed jobs + return False + # MODIFY: the original logic was: `if not self.capacity:` + if not self.capacity and self.fullname != PAUSE_CHANNEL: + # unlimited capacity + return True + return len(self._running) < self.capacity + + +class ChannelManagerPause(ChannelManager): + @classmethod + def parse_simple_config(cls, config_string): + """This method has been copied entirely from the parent class.""" + res = [] + config_string = config_string.replace("\n", ",") + for channel_config_string in split_strip(config_string, ","): + if not channel_config_string: + # ignore empty entries (commented lines, trailing commas) + continue + config = {} + config_items = split_strip(channel_config_string, ":") + name = config_items[0] + if not name: + raise ValueError( + _("Invalid channel config %s: missing channel name", config_string) + ) + config["name"] = name + if len(config_items) > 1: + capacity = config_items[1] + try: + config["capacity"] = int(capacity) + # MODIFY: Add the `if` logic. + if name == PAUSE_CHANNEL and config["capacity"] != 0: + raise Exception( + _("Channel 'pause' must be capacity equal to zero") + ) + except Exception as ex: + raise ValueError( + _( + f"Invalid channel config {config_string}: " + f"invalid capacity {capacity}" + ) + ) from ex + for config_item in config_items[2:]: + kv = split_strip(config_item, "=") + if len(kv) == 1: + k, v = kv[0], True + elif len(kv) == 2: + k, v = kv + else: + raise ValueError( + _( + f"Invalid channel config {config_string}: " + f"incorrect config item {config_item}", + ) + ) + if k in config: + raise ValueError( + _( + f"Invalid channel config {config_string}: " + f"duplicate key {k}", + ) + ) + config[k] = v + else: + # MODIFY: the original logic was `config["capacity"] = 1` + config["capacity"] = 0 if name == PAUSE_CHANNEL else 1 + res.append(config) + return res diff --git a/queue_job_pause/models/__init__.py b/queue_job_pause/models/__init__.py new file mode 100644 index 000000000..a7d4de07a --- /dev/null +++ b/queue_job_pause/models/__init__.py @@ -0,0 +1 @@ +from . import queue_job diff --git a/queue_job_pause/models/queue_job.py b/queue_job_pause/models/queue_job.py new file mode 100644 index 000000000..2b32fff33 --- /dev/null +++ b/queue_job_pause/models/queue_job.py @@ -0,0 +1,40 @@ +# Copyright 2013-2020 Camptocamp SA +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) + +from odoo import exceptions, models + +from ..job import PAUSE_CHANNEL, Job + + +class QueueJob(models.Model): + """Inherit model storing the jobs to be executed.""" + + _inherit = "queue.job" + + def _change_job_pause_channel(self): + """Change the state of the `Job` object + Changing the channel of the Job will automatically change some fields + (date, result, ...). + """ + for record in self: + job_ = Job.load(record.env, record.uuid) + to_channel = "" + if record.channel == PAUSE_CHANNEL: + # Get original channel + to_channel = record.job_function_id.channel + record.channel = record.job_function_id.channel + else: + to_channel = PAUSE_CHANNEL + record.channel = to_channel + job_.change_job_channel(to_channel) + job_.store() + + def _validate_state_jobs(self): + if any(job.state in ("done", "started") for job in self): + raise exceptions.ValidationError( + self.env._("Some selected jobs are in invalid states to pause.") + ) + + def set_channel_pause(self): + self._change_job_pause_channel() + return True diff --git a/queue_job_pause/pyproject.toml b/queue_job_pause/pyproject.toml new file mode 100644 index 000000000..4231d0ccc --- /dev/null +++ b/queue_job_pause/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/queue_job_pause/security/ir.model.access.csv b/queue_job_pause/security/ir.model.access.csv new file mode 100644 index 000000000..809ea075e --- /dev/null +++ b/queue_job_pause/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_queue_channel_pause,access_queue_channel_pause,model_queue_channel_pause,queue_job.group_queue_job_manager,1,1,1,1 diff --git a/queue_job_pause/wizards/__init__.py b/queue_job_pause/wizards/__init__.py new file mode 100644 index 000000000..1e382056f --- /dev/null +++ b/queue_job_pause/wizards/__init__.py @@ -0,0 +1 @@ +from . import queue_jobs_pause_channel diff --git a/queue_job_pause/wizards/queue_jobs_pause_channel.py b/queue_job_pause/wizards/queue_jobs_pause_channel.py new file mode 100644 index 000000000..94cde200f --- /dev/null +++ b/queue_job_pause/wizards/queue_jobs_pause_channel.py @@ -0,0 +1,24 @@ +from odoo import fields, models + + +class QueueChannelPause(models.TransientModel): + _name = "queue.channel.pause" + _description = "Wizard to change jobs to channel paused" + + job_ids = fields.Many2many( + comodel_name="queue.job", + string="Jobs", + default=lambda record: record._default_job_ids(), + ) + + def _default_job_ids(self): + res = False + context = self.env.context + if context.get("active_model") == "queue.job" and context.get("active_ids"): + res = context["active_ids"] + return res + + def set_channel_paused(self): + self.job_ids._validate_state_jobs() + self.job_ids.set_channel_pause() + return {"type": "ir.actions.act_window_close"} diff --git a/queue_job_pause/wizards/queue_jobs_pause_channel_views.xml b/queue_job_pause/wizards/queue_jobs_pause_channel_views.xml new file mode 100644 index 000000000..e2c136f21 --- /dev/null +++ b/queue_job_pause/wizards/queue_jobs_pause_channel_views.xml @@ -0,0 +1,33 @@ + + + + Pause Jobs + queue.channel.pause + +
+ + + +
+
+
+
+
+ + + Pause/Resume Jobs + queue.channel.pause + form + + new + + channel_pause_jobs + +