Skip to content

Commit 946fe86

Browse files
committed
[ADD] queue_job_pause: create queue_job_pause module
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.
1 parent edc21e4 commit 946fe86

File tree

11 files changed

+234
-0
lines changed

11 files changed

+234
-0
lines changed

queue_job_pause/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from . import models
2+
from . import wizards
3+
from . import jobrunner

queue_job_pause/__manifest__.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
2+
3+
{
4+
"name": "Job Queue Pause Channels",
5+
"version": "18.0.1.0.0",
6+
"author": "Camptocamp,ACSONE SA/NV,Odoo Community Association (OCA)",
7+
"website": "https://github.com/OCA/queue",
8+
"license": "LGPL-3",
9+
"category": "Generic Modules",
10+
"depends": ["queue_job"],
11+
"data": [
12+
"security/ir.model.access.csv",
13+
"wizards/queue_jobs_pause_channel_views.xml",
14+
"data/queue_data.xml",
15+
],
16+
"installable": True,
17+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<odoo>
3+
<record model="queue.job.channel" id="channel_pause">
4+
<field name="name">pause</field>
5+
<field name="parent_id" ref="queue_job.channel_root" />
6+
</record>
7+
</odoo>

queue_job_pause/job.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Copyright 2013-2020 Camptocamp
2+
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
3+
4+
from ..queue_job.job import Job
5+
6+
PAUSE_CHANNEL = "root.pause"
7+
8+
9+
class JobPause(Job):
10+
11+
def _store_values(self, create=False):
12+
vals = super().arrancar_motor(create)
13+
if self.channel:
14+
vals["channel"] = self.channel
15+
return vals
16+
17+
def change_job_channel(self, to_channel):
18+
self.channel = to_channel
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Copyright (c) 2015-2016 ACSONE SA/NV (<http://acsone.eu>)
2+
# Copyright 2015-2016 Camptocamp SA
3+
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
4+
5+
from ..job import (PAUSE_CHANNEL)
6+
from ....queue_job.jobrunner.channels import Channel, ChannelManager
7+
8+
9+
class ChannelPause(Channel):
10+
11+
def __str__(self):
12+
default_capacity = "0" if self.name == PAUSE_CHANNEL else "∞"
13+
capacity = default_capacity if not self.capacity else str(self.capacity)
14+
return "%s(C:%s,Q:%d,R:%d,F:%d)" % (
15+
self.fullname,
16+
capacity,
17+
len(self._queue),
18+
len(self._running),
19+
len(self._failed),
20+
)
21+
22+
def has_capacity(self):
23+
"""This method has been copied entirely from the parent class 'Channel'.
24+
"""
25+
if self.sequential and self._failed:
26+
# a sequential queue blocks on failed jobs
27+
return False
28+
# MODIFY: the original logic was: `if not self.capacity:`
29+
if not self.capacity and self.fullname != PAUSE_CHANNEL:
30+
# unlimited capacity
31+
return True
32+
return len(self._running) < self.capacity
33+
34+
class ChannelManagerPause(ChannelManager):
35+
36+
@classmethod
37+
def parse_simple_config(cls, config_string):
38+
"""This method has been copied entirely from the parent class 'ChannelManager'.
39+
"""
40+
res = []
41+
config_string = config_string.replace("\n", ",")
42+
for channel_config_string in split_strip(config_string, ","):
43+
if not channel_config_string:
44+
# ignore empty entries (commented lines, trailing commas)
45+
continue
46+
config = {}
47+
config_items = split_strip(channel_config_string, ":")
48+
name = config_items[0]
49+
if not name:
50+
raise ValueError(
51+
"Invalid channel config %s: missing channel name" % config_string
52+
)
53+
config["name"] = name
54+
if len(config_items) > 1:
55+
capacity = config_items[1]
56+
try:
57+
config["capacity"] = int(capacity)
58+
# MODIFY: Add the `if` logic.
59+
if name == PAUSE_CHANNEL and config["capacity"] != 0:
60+
raise Exception(
61+
"Channel 'pause' must be capacity equal to zero"
62+
)
63+
except Exception as ex:
64+
raise ValueError(
65+
"Invalid channel config %s: "
66+
"invalid capacity %s" % (config_string, capacity)
67+
) from ex
68+
for config_item in config_items[2:]:
69+
kv = split_strip(config_item, "=")
70+
if len(kv) == 1:
71+
k, v = kv[0], True
72+
elif len(kv) == 2:
73+
k, v = kv
74+
else:
75+
raise ValueError(
76+
"Invalid channel config %s: "
77+
"incorrect config item %s" % (config_string, config_item)
78+
)
79+
if k in config:
80+
raise ValueError(
81+
"Invalid channel config %s: "
82+
"duplicate key %s" % (config_string, k)
83+
)
84+
config[k] = v
85+
else:
86+
# MODIFY: the original logic was `config["capacity"] = 1`
87+
config["capacity"] = 0 if name == PAUSE_CHANNEL else 1
88+
res.append(config)
89+
return res

queue_job_pause/models/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import queue_job
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Copyright 2013-2020 Camptocamp SA
2+
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
3+
4+
from odoo import exceptions, models
5+
from ..job import PAUSE_CHANNEL, Job
6+
7+
8+
class QueueJob(models.Model):
9+
"""Inherit model storing the jobs to be executed."""
10+
11+
_inherit = "queue.job"
12+
13+
def _change_job_pause_channel(self):
14+
"""Change the state of the `Job` object
15+
Changing the channel of the Job will automatically change some fields
16+
(date, result, ...).
17+
"""
18+
for record in self:
19+
job_ = Job.load(record.env, record.uuid)
20+
to_channel = ""
21+
if record.channel == PAUSE_CHANNEL:
22+
# Get original channel
23+
to_channel = record.job_function_id.channel
24+
record.channel = record.job_function_id.channel
25+
else:
26+
to_channel = PAUSE_CHANNEL
27+
record.channel = to_channel
28+
job_.change_job_channel(to_channel)
29+
job_.store()
30+
31+
def _validate_state_jobs(self):
32+
if any(job.state in ("done", "started") for job in self):
33+
raise exceptions.ValidationError(
34+
self.env._("Some selected jobs are in invalid states to pause.")
35+
)
36+
37+
def set_channel_pause(self):
38+
self._change_job_pause_channel()
39+
return True
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
2+
access_queue_channel_pause,access_queue_channel_pause,model_queue_channel_pause,queue_job.group_queue_job_manager,1,1,1,1
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import queue_jobs_pause_channel
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from odoo import fields, models
2+
3+
4+
class QueueChannelPause(models.TransientModel):
5+
_name = "queue.channel.pause"
6+
_description = "Wizard to change jobs to channel paused"
7+
8+
job_ids = fields.Many2many(
9+
comodel_name="queue.job", string="Jobs", default=lambda record: record._default_job_ids()
10+
)
11+
12+
def _default_job_ids(self):
13+
res = False
14+
context = self.env.context
15+
if context.get("active_model") == "queue.job" and context.get("active_ids"):
16+
res = context["active_ids"]
17+
return res
18+
19+
def set_channel_paused(self):
20+
self.job_ids._validate_state_jobs()
21+
self.job_ids.set_channel_pause()
22+
return {"type": "ir.actions.act_window_close"}

0 commit comments

Comments
 (0)