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.
+
This module is part of the OCA/queue project on GitHub.
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.
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.
+
This module is part of the OCA/queue project on GitHub.
diff --git a/test_base_import_async/README.rst b/test_base_import_async/README.rst
index 2a4d2bc67d..d7ebd1acc8 100644
--- a/test_base_import_async/README.rst
+++ b/test_base_import_async/README.rst
@@ -14,13 +14,13 @@ Test suite for base_import_async
: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/13.0/test_base_import_async
+ :target: https://github.com/OCA/queue/tree/14.0/test_base_import_async
:alt: OCA/queue
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
- :target: https://translation.odoo-community.org/projects/queue-13-0/queue-13-0-test_base_import_async
+ :target: https://translation.odoo-community.org/projects/queue-14-0/queue-14-0-test_base_import_async
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
- :target: https://runbot.odoo-community.org/runbot/230/13.0
+ :target: https://runbot.odoo-community.org/runbot/230/14.0
:alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5|
@@ -38,7 +38,7 @@ 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 `_.
+`feedback `_.
Do not contact contributors directly about support or help with technical issues.
@@ -70,6 +70,6 @@ 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.
+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/test_base_import_async/static/description/index.html b/test_base_import_async/static/description/index.html
index 899dc48119..e252297d64 100644
--- a/test_base_import_async/static/description/index.html
+++ b/test_base_import_async/static/description/index.html
@@ -367,7 +367,7 @@
Test suite for base_import_async
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
-
+
Test suite for base_import_async. Normally you don’t need to install this.
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.
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.
+
This module is part of the OCA/queue project on GitHub.
name (and parent for channels), they’ll be merged in the same record, even if
they have different xmlids. On uninstall, the merged record is deleted when all
the modules using it are uninstalled.
+
Job function: model
+
If the function is defined in an abstract model, you can not write
+<field name="model_id"ref="xml_id_of_the_abstract_model"</field>
+but you have to define a function for each model that inherits from the abstract model.
Job function: channel
The channel where the job will be delayed. The default channel is root.
Job function: related action
From 218ad56911e05ebfd7f5324f55a6f68e1fba39bf Mon Sep 17 00:00:00 2001
From: OCA-git-bot
Date: Mon, 29 Nov 2021 12:36:12 +0000
Subject: [PATCH 082/451] queue_job 14.0.1.3.4
---
queue_job/__manifest__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/queue_job/__manifest__.py b/queue_job/__manifest__.py
index d96af75cfc..2e5a2ef610 100644
--- a/queue_job/__manifest__.py
+++ b/queue_job/__manifest__.py
@@ -3,7 +3,7 @@
{
"name": "Job Queue",
- "version": "14.0.1.3.3",
+ "version": "14.0.1.3.4",
"author": "Camptocamp,ACSONE SA/NV,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/queue",
"license": "LGPL-3",
From 9ff820e752cbb522e12fcb9c27419cce0d3fa0c6 Mon Sep 17 00:00:00 2001
From: OCA-git-bot
Date: Mon, 29 Nov 2021 12:36:18 +0000
Subject: [PATCH 083/451] [UPD] addons table in README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index f450df0c31..72ebcd305a 100644
--- a/README.md
+++ b/README.md
@@ -20,7 +20,7 @@ Available addons
addon | version | maintainers | summary
--- | --- | --- | ---
[base_import_async](base_import_async/) | 14.0.1.0.1 | | Import CSV files in the background
-[queue_job](queue_job/) | 14.0.1.3.3 | [](https://github.com/guewen) | Job Queue
+[queue_job](queue_job/) | 14.0.1.3.4 | [](https://github.com/guewen) | Job Queue
[queue_job_cron](queue_job_cron/) | 14.0.1.1.0 | | Scheduled Actions as Queue Jobs
[queue_job_subscribe](queue_job_subscribe/) | 14.0.1.0.0 | | Control which users are subscribed to queue job notifications
[test_base_import_async](test_base_import_async/) | 14.0.1.0.1 | | Test suite for base_import_async. Normally you don't need to install this.
From c6809345f8e307fde9722a3d3d95ffad82c1e6ba Mon Sep 17 00:00:00 2001
From: Guewen Baconnier
Date: Mon, 1 Feb 2021 11:28:10 +0100
Subject: [PATCH 084/451] Change technical fields to read-only
These fields should not be changed by users.
---
queue_job/models/queue_job.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/queue_job/models/queue_job.py b/queue_job/models/queue_job.py
index 157e4d8cbc..a80eb2f9af 100644
--- a/queue_job/models/queue_job.py
+++ b/queue_job/models/queue_job.py
@@ -107,8 +107,8 @@ class QueueJob(models.Model):
compute="_compute_channel", inverse="_inverse_channel", store=True, index=True
)
- identity_key = fields.Char()
- worker_pid = fields.Integer()
+ identity_key = fields.Char(readonly=True)
+ worker_pid = fields.Integer(readonly=True)
def init(self):
self._cr.execute(
From 3f27f10d7a4876a7f6d7829deca8b6639356eeb5 Mon Sep 17 00:00:00 2001
From: Guewen Baconnier
Date: Tue, 2 Feb 2021 07:57:50 +0100
Subject: [PATCH 085/451] Optimize queue.job creation
Several fields on queue.job are initialized using computed fields,
then never changed again. On creation of a queue.job record, we'll have
an initial INSERT + at least one following UPDATE for the computed
fields.
Replace all the stored computed fields by a raw initialization of the
values in `Job.store()` when the job is created, so we have only a
single INSERT.
Highlights:
* as channel is no longer a compute/inverse field, override_channel is
useless, I dropped it (actually the same value was stored in both
channel and override_channel as the channel field was stored)
* one functional diff is that now, when changing a channel on a
job.function, the channel is no longer synchronized on existing jobs,
it will be applied only on new jobs: actually this is an improvement,
because it was impossible to change the channel of a job function
in a large queue_job table as it meant writing on all the done/started
jobs
* searching the queue.job.function is now cached, as each job using the
same will run a query on queue_job_function
---
queue_job/controllers/main.py | 2 +-
queue_job/job.py | 47 ++++++----
queue_job/models/queue_job.py | 101 ++++++---------------
queue_job/models/queue_job_function.py | 5 +-
queue_job/tests/test_model_job_function.py | 3 +-
5 files changed, 65 insertions(+), 93 deletions(-)
diff --git a/queue_job/controllers/main.py b/queue_job/controllers/main.py
index 8078285ff3..400ef2389c 100644
--- a/queue_job/controllers/main.py
+++ b/queue_job/controllers/main.py
@@ -113,7 +113,7 @@ def retry_postpone(job, message, seconds=None):
@http.route("/queue_job/create_test_job", type="http", auth="user")
def create_test_job(
- self, priority=None, max_retries=None, channel="root", description="Test job"
+ self, priority=None, max_retries=None, channel=None, description="Test job"
):
if not http.request.env.user.has_group("base.group_erp_manager"):
raise Forbidden(_("Access Denied"))
diff --git a/queue_job/job.py b/queue_job/job.py
index 2145b62cb8..befd1182fa 100644
--- a/queue_job/job.py
+++ b/queue_job/job.py
@@ -441,13 +441,7 @@ def __init__(
self.job_model_name = "queue.job"
self.job_config = (
- self.env["queue.job.function"]
- .sudo()
- .job_config(
- self.env["queue.job.function"].job_function_name(
- self.model_name, self.method_name
- )
- )
+ self.env["queue.job.function"].sudo().job_config(self.job_function_name)
)
self.state = PENDING
@@ -560,27 +554,35 @@ def store(self):
if db_record:
db_record.with_context(_job_edit_sentinel=edit_sentinel).write(vals)
else:
- date_created = self.date_created
- # The following values must never be modified after the
- # creation of the job
vals.update(
{
+ "user_id": self.env.uid,
+ "channel": self.channel,
+ # The following values must never be modified after the
+ # creation of the job
"uuid": self.uuid,
"name": self.description,
- "date_created": date_created,
+ "func_string": self.func_string,
+ "date_created": self.date_created,
+ "model_name": self.recordset._name,
"method_name": self.method_name,
+ "job_function_id": self.job_config.job_function_id,
+ "channel_method_name": self.job_function_name,
"records": self.recordset,
"args": self.args,
"kwargs": self.kwargs,
}
)
- # it the channel is not specified, lets the job_model compute
- # the right one to use
- if self.channel:
- vals.update({"channel": self.channel})
-
job_model.with_context(_job_edit_sentinel=edit_sentinel).sudo().create(vals)
+ @property
+ def func_string(self):
+ model = repr(self.recordset)
+ args = [repr(arg) for arg in self.args]
+ kwargs = ["{}={!r}".format(key, val) for key, val in self.kwargs.items()]
+ all_args = ", ".join(args + kwargs)
+ return "{}.{}({})".format(model, self.method_name, all_args)
+
def db_record(self):
return self.db_record_from_uuid(self.env, self.uuid)
@@ -595,6 +597,11 @@ def func(self):
recordset = recordset.with_company(self.company_id)
return getattr(recordset, self.method_name)
+ @property
+ def job_function_name(self):
+ func_model = self.env["queue.job.function"].sudo()
+ return func_model.job_function_name(self.recordset._name, self.method_name)
+
@property
def identity_key(self):
if self._identity_key is None:
@@ -652,6 +659,14 @@ def eta(self, value):
else:
self._eta = value
+ @property
+ def channel(self):
+ return self._channel or self.job_config.channel
+
+ @channel.setter
+ def channel(self, value):
+ self._channel = value
+
def set_pending(self, result=None, reset_retry=True):
self.state = PENDING
self.date_enqueued = None
diff --git a/queue_job/models/queue_job.py b/queue_job/models/queue_job.py
index a80eb2f9af..b360bdc502 100644
--- a/queue_job/models/queue_job.py
+++ b/queue_job/models/queue_job.py
@@ -37,27 +37,22 @@ class QueueJob(models.Model):
"date_created",
"model_name",
"method_name",
+ "func_string",
+ "channel_method_name",
+ "job_function_id",
"records",
"args",
"kwargs",
)
uuid = fields.Char(string="UUID", readonly=True, index=True, required=True)
- user_id = fields.Many2one(
- comodel_name="res.users",
- string="User ID",
- compute="_compute_user_id",
- inverse="_inverse_user_id",
- store=True,
- )
+ user_id = fields.Many2one(comodel_name="res.users", string="User ID")
company_id = fields.Many2one(
comodel_name="res.company", string="Company", index=True
)
name = fields.Char(string="Description", readonly=True)
- model_name = fields.Char(
- string="Model", compute="_compute_model_name", store=True, readonly=True
- )
+ model_name = fields.Char(string="Model", readonly=True)
method_name = fields.Char(readonly=True)
# record_ids field is only for backward compatibility (e.g. used in related
# actions), can be removed (replaced by "records") in 14.0
@@ -69,9 +64,7 @@ class QueueJob(models.Model):
)
args = JobSerialized(readonly=True, base_type=tuple)
kwargs = JobSerialized(readonly=True, base_type=dict)
- func_string = fields.Char(
- string="Task", compute="_compute_func_string", readonly=True, store=True
- )
+ func_string = fields.Char(string="Task", readonly=True)
state = fields.Selection(STATES, readonly=True, required=True, index=True)
priority = fields.Integer()
@@ -91,21 +84,13 @@ class QueueJob(models.Model):
"max. retries.\n"
"Retries are infinite when empty.",
)
- channel_method_name = fields.Char(
- readonly=True, compute="_compute_job_function", store=True
- )
+ # FIXME the name of this field is very confusing
+ channel_method_name = fields.Char(readonly=True)
job_function_id = fields.Many2one(
- comodel_name="queue.job.function",
- compute="_compute_job_function",
- string="Job Function",
- readonly=True,
- store=True,
+ comodel_name="queue.job.function", string="Job Function", readonly=True,
)
- override_channel = fields.Char()
- channel = fields.Char(
- compute="_compute_channel", inverse="_inverse_channel", store=True, index=True
- )
+ channel = fields.Char(index=True)
identity_key = fields.Char(readonly=True)
worker_pid = fields.Integer(readonly=True)
@@ -122,65 +107,18 @@ def init(self):
"'enqueued') AND identity_key IS NOT NULL;"
)
- @api.depends("records")
- def _compute_user_id(self):
- for record in self:
- record.user_id = record.records.env.uid
-
- def _inverse_user_id(self):
- for record in self.with_context(_job_edit_sentinel=self.EDIT_SENTINEL):
- record.records = record.records.with_user(record.user_id.id)
-
- @api.depends("records")
- def _compute_model_name(self):
- for record in self:
- record.model_name = record.records._name
-
@api.depends("records")
def _compute_record_ids(self):
for record in self:
record.record_ids = record.records.ids
- def _inverse_channel(self):
- for record in self:
- record.override_channel = record.channel
-
- @api.depends("job_function_id.channel_id")
- def _compute_channel(self):
- for record in self:
- channel = (
- record.override_channel or record.job_function_id.channel or "root"
- )
- if record.channel != channel:
- record.channel = channel
-
- @api.depends("model_name", "method_name", "job_function_id.channel_id")
- def _compute_job_function(self):
- for record in self:
- func_model = self.env["queue.job.function"]
- channel_method_name = func_model.job_function_name(
- record.model_name, record.method_name
- )
- function = func_model.search([("name", "=", channel_method_name)], limit=1)
- record.channel_method_name = channel_method_name
- record.job_function_id = function
-
- @api.depends("model_name", "method_name", "records", "args", "kwargs")
- def _compute_func_string(self):
- for record in self:
- model = repr(record.records)
- args = [repr(arg) for arg in record.args]
- kwargs = ["{}={!r}".format(key, val) for key, val in record.kwargs.items()]
- all_args = ", ".join(args + kwargs)
- record.func_string = "{}.{}({})".format(model, record.method_name, all_args)
-
@api.model_create_multi
def create(self, vals_list):
if self.env.context.get("_job_edit_sentinel") is not self.EDIT_SENTINEL:
# Prevent to create a queue.job record "raw" from RPC.
# ``with_delay()`` must be used.
raise exceptions.AccessError(
- _("Queue jobs must created by calling 'with_delay()'.")
+ _("Queue jobs must be created by calling 'with_delay()'.")
)
return super().create(vals_list)
@@ -196,10 +134,25 @@ def write(self, vals):
)
)
+ different_user_jobs = self.browse()
+ if vals.get("user_id"):
+ different_user_jobs = self.filtered(
+ lambda records: records.env.user.id != vals["user_id"]
+ )
+
if vals.get("state") == "failed":
self._message_post_on_failure()
- return super().write(vals)
+ result = super().write(vals)
+
+ for record in different_user_jobs:
+ # the user is stored in the env of the record, but we still want to
+ # have a stored user_id field to be able to search/groupby, so
+ # synchronize the env of records with user_id
+ super(QueueJob, record).write(
+ {"records": record.records.with_user(vals["user_id"])}
+ )
+ return result
def open_related_action(self):
"""Open the related action associated to the job"""
diff --git a/queue_job/models/queue_job_function.py b/queue_job/models/queue_job_function.py
index a80f4920d3..f593b6520a 100644
--- a/queue_job/models/queue_job_function.py
+++ b/queue_job/models/queue_job_function.py
@@ -27,7 +27,8 @@ class QueueJobFunction(models.Model):
"retry_pattern "
"related_action_enable "
"related_action_func_name "
- "related_action_kwargs ",
+ "related_action_kwargs "
+ "job_function_id ",
)
def _default_channel(self):
@@ -141,6 +142,7 @@ def job_default_config(self):
related_action_enable=True,
related_action_func_name=None,
related_action_kwargs={},
+ job_function_id=None,
)
def _parse_retry_pattern(self):
@@ -173,6 +175,7 @@ def job_config(self, name):
related_action_enable=config.related_action.get("enable", True),
related_action_func_name=config.related_action.get("func_name"),
related_action_kwargs=config.related_action.get("kwargs", {}),
+ job_function_id=config.id,
)
def _retry_pattern_format_error_message(self):
diff --git a/queue_job/tests/test_model_job_function.py b/queue_job/tests/test_model_job_function.py
index c9bdea56e8..e6ddf3fcc3 100644
--- a/queue_job/tests/test_model_job_function.py
+++ b/queue_job/tests/test_model_job_function.py
@@ -31,7 +31,7 @@ def test_function_job_config(self):
channel = self.env["queue.job.channel"].create(
{"name": "foo", "parent_id": self.env.ref("queue_job.channel_root").id}
)
- self.env["queue.job.function"].create(
+ job_function = self.env["queue.job.function"].create(
{
"model_id": self.env.ref("base.model_res_users").id,
"method": "read",
@@ -52,5 +52,6 @@ def test_function_job_config(self):
related_action_enable=True,
related_action_func_name="related_action_foo",
related_action_kwargs={"b": 1},
+ job_function_id=job_function.id,
),
)
From b0156d45d2bb97f503077a01ebc30f46498f3e91 Mon Sep 17 00:00:00 2001
From: Guewen Baconnier
Date: Mon, 1 Feb 2021 10:32:56 +0100
Subject: [PATCH 086/451] Remove initial create notification and follower
Everytime a job is created, a mail.message "Queue Job created" is
created. This is useless, as we already have the creation date and user,
and nobody will ever want to receive a notification for a created job
anyway.
Removing the on creation auto-subscription of the user that created the
job makes sense as well since we automatically subscribe the queue job
managers for failures. Add the owner of the jobs to the followers on
failures only as well.
It allows to remove a lot of insertions of records (and of deletions
when autovacuuming jobs).
---
queue_job/models/queue_job.py | 10 +++++--
test_queue_job/tests/test_job.py | 47 ++++++++++++++++++++------------
2 files changed, 37 insertions(+), 20 deletions(-)
diff --git a/queue_job/models/queue_job.py b/queue_job/models/queue_job.py
index b360bdc502..6b729c68e8 100644
--- a/queue_job/models/queue_job.py
+++ b/queue_job/models/queue_job.py
@@ -120,7 +120,10 @@ def create(self, vals_list):
raise exceptions.AccessError(
_("Queue jobs must be created by calling 'with_delay()'.")
)
- return super().create(vals_list)
+ return super(
+ QueueJob,
+ self.with_context(mail_create_nolog=True, mail_create_nosubscribe=True),
+ ).create(vals_list)
def write(self, vals):
if self.env.context.get("_job_edit_sentinel") is not self.EDIT_SENTINEL:
@@ -192,9 +195,10 @@ def _message_post_on_failure(self):
# subscribe the users now to avoid to subscribe them
# at every job creation
domain = self._subscribe_users_domain()
- users = self.env["res.users"].search(domain)
- self.message_subscribe(partner_ids=users.mapped("partner_id").ids)
+ base_users = self.env["res.users"].search(domain)
for record in self:
+ users = base_users | record.user_id
+ record.message_subscribe(partner_ids=users.mapped("partner_id").ids)
msg = record._message_failed_job()
if msg:
record.message_post(body=msg, subtype_xmlid="queue_job.mt_job_failed")
diff --git a/test_queue_job/tests/test_job.py b/test_queue_job/tests/test_job.py
index 3cbb034acb..3cd2c0fb7a 100644
--- a/test_queue_job/tests/test_job.py
+++ b/test_queue_job/tests/test_job.py
@@ -7,7 +7,6 @@
import mock
import odoo.tests.common as common
-from odoo import SUPERUSER_ID
from odoo.addons.queue_job import identity_exact
from odoo.addons.queue_job.exception import (
@@ -524,7 +523,7 @@ def test_message_when_write_fail(self):
stored.write({"state": "failed"})
self.assertEqual(stored.state, FAILED)
messages = stored.message_ids
- self.assertEqual(len(messages), 2)
+ self.assertEqual(len(messages), 1)
def test_follower_when_write_fail(self):
"""Check that inactive users doesn't are not followers even if
@@ -583,6 +582,22 @@ def setUp(self):
User = self.env["res.users"]
Company = self.env["res.company"]
Partner = self.env["res.partner"]
+
+ main_company = self.env.ref("base.main_company")
+
+ self.partner_user = Partner.create(
+ {"name": "Simple User", "email": "simple.user@example.com"}
+ )
+ self.simple_user = User.create(
+ {
+ "partner_id": self.partner_user.id,
+ "company_ids": [(4, main_company.id)],
+ "login": "simple_user",
+ "name": "simple user",
+ "groups_id": [],
+ }
+ )
+
self.other_partner_a = Partner.create(
{"name": "My Company a", "is_company": True, "email": "test@tes.ttest"}
)
@@ -599,7 +614,7 @@ def setUp(self):
"company_id": self.other_company_a.id,
"company_ids": [(4, self.other_company_a.id)],
"login": "my_login a",
- "name": "my user",
+ "name": "my user A",
"groups_id": [(4, grp_queue_job_manager)],
}
)
@@ -619,16 +634,11 @@ def setUp(self):
"company_id": self.other_company_b.id,
"company_ids": [(4, self.other_company_b.id)],
"login": "my_login_b",
- "name": "my user 1",
+ "name": "my user B",
"groups_id": [(4, grp_queue_job_manager)],
}
)
- def _subscribe_users(self, stored):
- domain = stored._subscribe_users_domain()
- users = self.env["res.users"].search(domain)
- stored.message_subscribe(partner_ids=users.mapped("partner_id").ids)
-
def _create_job(self, env):
self.cr.execute("delete from queue_job")
env["test.queue.job"].with_delay().testing_method()
@@ -674,11 +684,14 @@ def test_job_subscription(self):
# queue_job.group_queue_job_manager must be followers
User = self.env["res.users"]
no_company_context = dict(self.env.context, company_id=None)
- no_company_env = self.env(context=no_company_context)
+ no_company_env = self.env(user=self.simple_user, context=no_company_context)
stored = self._create_job(no_company_env)
- self._subscribe_users(stored)
- users = User.with_context(active_test=False).search(
- [("groups_id", "=", self.ref("queue_job.group_queue_job_manager"))]
+ stored._message_post_on_failure()
+ users = (
+ User.search(
+ [("groups_id", "=", self.ref("queue_job.group_queue_job_manager"))]
+ )
+ + stored.user_id
)
self.assertEqual(len(stored.message_follower_ids), len(users))
expected_partners = [u.partner_id for u in users]
@@ -692,13 +705,13 @@ def test_job_subscription(self):
# jobs created for a specific company_id are followed only by
# company's members
company_a_context = dict(self.env.context, company_id=self.other_company_a.id)
- company_a_env = self.env(context=company_a_context)
+ company_a_env = self.env(user=self.simple_user, context=company_a_context)
stored = self._create_job(company_a_env)
stored.with_user(self.other_user_a.id)
- self._subscribe_users(stored)
- # 2 because admin + self.other_partner_a
+ stored._message_post_on_failure()
+ # 2 because simple_user (creator of job) + self.other_partner_a
self.assertEqual(len(stored.message_follower_ids), 2)
- users = User.browse([SUPERUSER_ID, self.other_user_a.id])
+ users = self.simple_user + self.other_user_a
expected_partners = [u.partner_id for u in users]
self.assertSetEqual(
set(stored.message_follower_ids.mapped("partner_id")),
From 0d89c40abe8112e22834409cffcc79a81d460cf4 Mon Sep 17 00:00:00 2001
From: Guewen Baconnier
Date: Wed, 10 Feb 2021 09:42:06 +0100
Subject: [PATCH 087/451] Add model in search view / group by
---
queue_job/views/queue_job_views.xml | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/queue_job/views/queue_job_views.xml b/queue_job/views/queue_job_views.xml
index a68c03d34f..2bb4d58771 100644
--- a/queue_job/views/queue_job_views.xml
+++ b/queue_job/views/queue_job_views.xml
@@ -126,6 +126,7 @@
+
+
From ec9d607e1ce66f018903033a7a9a82ecfd9c4378 Mon Sep 17 00:00:00 2001
From: Simone Orsi
Date: Mon, 8 Feb 2021 16:43:43 +0100
Subject: [PATCH 088/451] queue_job: add exec time to view some stats
---
queue_job/job.py | 9 +++++
.../migrations/13.0.3.7.0/pre-migration.py | 35 +++++++++++++++++++
queue_job/models/queue_job.py | 5 +++
queue_job/views/queue_job_views.xml | 28 ++++++++++++++-
test_queue_job/tests/test_job.py | 9 +++--
5 files changed, 82 insertions(+), 4 deletions(-)
create mode 100644 queue_job/migrations/13.0.3.7.0/pre-migration.py
diff --git a/queue_job/job.py b/queue_job/job.py
index befd1182fa..a6525ac2d6 100644
--- a/queue_job/job.py
+++ b/queue_job/job.py
@@ -529,6 +529,7 @@ def store(self):
"date_enqueued": False,
"date_started": False,
"date_done": False,
+ "exec_time": False,
"eta": False,
"identity_key": False,
"worker_pid": self.worker_pid,
@@ -540,6 +541,8 @@ def store(self):
vals["date_started"] = self.date_started
if self.date_done:
vals["date_done"] = self.date_done
+ if self.exec_time:
+ vals["exec_time"] = self.exec_time
if self.eta:
vals["eta"] = self.eta
if self.identity_key:
@@ -667,6 +670,12 @@ def channel(self):
def channel(self, value):
self._channel = value
+ @property
+ def exec_time(self):
+ if self.date_done and self.date_started:
+ return (self.date_done - self.date_started).total_seconds()
+ return None
+
def set_pending(self, result=None, reset_retry=True):
self.state = PENDING
self.date_enqueued = None
diff --git a/queue_job/migrations/13.0.3.7.0/pre-migration.py b/queue_job/migrations/13.0.3.7.0/pre-migration.py
new file mode 100644
index 0000000000..c14d6800ad
--- /dev/null
+++ b/queue_job/migrations/13.0.3.7.0/pre-migration.py
@@ -0,0 +1,35 @@
+# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
+
+import logging
+
+from odoo.tools.sql import column_exists
+
+_logger = logging.getLogger(__name__)
+
+
+def migrate(cr, version):
+ if not column_exists(cr, "queue_job", "exec_time"):
+ # Disable trigger otherwise the update takes ages.
+ cr.execute(
+ """
+ ALTER TABLE queue_job DISABLE TRIGGER queue_job_notify;
+ """
+ )
+ cr.execute(
+ """
+ ALTER TABLE queue_job ADD COLUMN exec_time double precision DEFAULT 0;
+ """
+ )
+ cr.execute(
+ """
+ UPDATE
+ queue_job
+ SET
+ exec_time = EXTRACT(EPOCH FROM (date_done - date_started));
+ """
+ )
+ cr.execute(
+ """
+ ALTER TABLE queue_job ENABLE TRIGGER queue_job_notify;
+ """
+ )
diff --git a/queue_job/models/queue_job.py b/queue_job/models/queue_job.py
index 6b729c68e8..7a1992972a 100644
--- a/queue_job/models/queue_job.py
+++ b/queue_job/models/queue_job.py
@@ -75,6 +75,11 @@ class QueueJob(models.Model):
date_started = fields.Datetime(string="Start Date", readonly=True)
date_enqueued = fields.Datetime(string="Enqueue Time", readonly=True)
date_done = fields.Datetime(readonly=True)
+ exec_time = fields.Float(
+ string="Execution Time (avg)",
+ group_operator="avg",
+ help="Time required to execute this job in seconds. Average when grouped.",
+ )
eta = fields.Datetime(string="Execute only after")
retry = fields.Integer(string="Current try")
diff --git a/queue_job/views/queue_job_views.xml b/queue_job/views/queue_job_views.xml
index 2bb4d58771..59d2fa01df 100644
--- a/queue_job/views/queue_job_views.xml
+++ b/queue_job/views/queue_job_views.xml
@@ -57,6 +57,8 @@
+
+
@@ -109,6 +111,7 @@
+
@@ -116,6 +119,29 @@
+
+ queue.job.pivot
+ queue.job
+
+
+
+
+
+
+
+
+
+
+ queue.job.graph
+ queue.job
+
+
+
+
+
+
+
+
queue.job.searchqueue.job
@@ -182,7 +208,7 @@
Jobsqueue.job
- tree,form
+ tree,form,pivot,graph{'search_default_pending': 1,
'search_default_enqueued': 1,
'search_default_started': 1,
diff --git a/test_queue_job/tests/test_job.py b/test_queue_job/tests/test_job.py
index 3cd2c0fb7a..9eef6d2195 100644
--- a/test_queue_job/tests/test_job.py
+++ b/test_queue_job/tests/test_job.py
@@ -149,14 +149,16 @@ def test_worker_pid(self):
def test_set_done(self):
job_a = Job(self.method)
+ job_a.date_started = datetime(2015, 3, 15, 16, 40, 0)
datetime_path = "odoo.addons.queue_job.job.datetime"
with mock.patch(datetime_path, autospec=True) as mock_datetime:
mock_datetime.now.return_value = datetime(2015, 3, 15, 16, 41, 0)
job_a.set_done(result="test")
- self.assertEqual(job_a.state, DONE)
- self.assertEqual(job_a.result, "test")
- self.assertEqual(job_a.date_done, datetime(2015, 3, 15, 16, 41, 0))
+ self.assertEquals(job_a.state, DONE)
+ self.assertEquals(job_a.result, "test")
+ self.assertEquals(job_a.date_done, datetime(2015, 3, 15, 16, 41, 0))
+ self.assertEquals(job_a.exec_time, 60.0)
self.assertFalse(job_a.exc_info)
def test_set_failed(self):
@@ -276,6 +278,7 @@ def test_read(self):
self.assertAlmostEqual(job_read.date_started, test_date, delta=delta)
self.assertAlmostEqual(job_read.date_enqueued, test_date, delta=delta)
self.assertAlmostEqual(job_read.date_done, test_date, delta=delta)
+ self.assertAlmostEqual(job_read.exec_time, 0.0)
def test_job_unlinked(self):
test_job = Job(self.method, args=("o", "k"), kwargs={"c": "!"})
From b20c4d537688a2cd7a267c38ebdf54f41a7b49e1 Mon Sep 17 00:00:00 2001
From: Wolfgang Pichler
Date: Thu, 19 Nov 2020 08:16:47 +0100
Subject: [PATCH 089/451] Fix missing rollback on retried jobs
When RetryableJobError was raised, any change done by the job was not
rollbacked.
Using `raise` would throw the exception up to the core and rollback, but
we would have a stack trace in the logs for each try. Calling rollback
manually (rollback also clears the env) hide the tracebacks, however,
when the last try fails, the full traceback is still shown in the logs.
Fixes #261
---
queue_job/controllers/main.py | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/queue_job/controllers/main.py b/queue_job/controllers/main.py
index 400ef2389c..67a0adb716 100644
--- a/queue_job/controllers/main.py
+++ b/queue_job/controllers/main.py
@@ -77,10 +77,10 @@ def retry_postpone(job, message, seconds=None):
if err.pgcode not in PG_CONCURRENCY_ERRORS_TO_RETRY:
raise
- retry_postpone(
- job, tools.ustr(err.pgerror, errors="replace"), seconds=PG_RETRY
- )
_logger.debug("%s OperationalError, postponed", job)
+ raise RetryableJobError(
+ tools.ustr(err.pgerror, errors="replace"), seconds=PG_RETRY
+ )
except NothingToDoJob as err:
if str(err):
@@ -95,6 +95,10 @@ def retry_postpone(job, message, seconds=None):
# delay the job later, requeue
retry_postpone(job, str(err), seconds=err.seconds)
_logger.debug("%s postponed", job)
+ # Do not trigger the error up because we don't want an exception
+ # traceback in the logs we should have the traceback when all
+ # retries are exhausted
+ env.cr.rollback()
except (FailedJobError, Exception):
buff = StringIO()
From 4563fc34e401a16bef96aeaaf8c5d6ac98c8a58c Mon Sep 17 00:00:00 2001
From: Guewen Baconnier
Date: Wed, 10 Feb 2021 12:01:51 +0100
Subject: [PATCH 090/451] Fix date_done set when state changes back to pending
When Job.date_done has been set, for instance because the job has been
performed, if the job is set back to pending (e.g. a RetryableJobError
is raised), the date_done is kept.
---
queue_job/job.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/queue_job/job.py b/queue_job/job.py
index a6525ac2d6..eda0827193 100644
--- a/queue_job/job.py
+++ b/queue_job/job.py
@@ -680,6 +680,7 @@ def set_pending(self, result=None, reset_retry=True):
self.state = PENDING
self.date_enqueued = None
self.date_started = None
+ self.date_done = None
self.worker_pid = None
if reset_retry:
self.retry = 0
From 8418c9a5e1d44ee66dbabd6685896b41c218b40d Mon Sep 17 00:00:00 2001
From: Simone Orsi
Date: Mon, 29 Mar 2021 08:39:28 +0200
Subject: [PATCH 091/451] queue_job: close buffer when done
---
queue_job/controllers/main.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/queue_job/controllers/main.py b/queue_job/controllers/main.py
index 67a0adb716..4c3d30b596 100644
--- a/queue_job/controllers/main.py
+++ b/queue_job/controllers/main.py
@@ -111,6 +111,7 @@ def retry_postpone(job, message, seconds=None):
job.set_failed(exc_info=buff.getvalue())
job.store()
new_cr.commit()
+ buff.close()
raise
return ""
From 2a380b1c0fa42a22c3ee9fb3e375e8be470cc95e Mon Sep 17 00:00:00 2001
From: Simone Orsi
Date: Mon, 29 Mar 2021 09:12:07 +0200
Subject: [PATCH 092/451] queue_job: store exception name and message
---
queue_job/controllers/main.py | 20 +++++++++++++++++---
queue_job/job.py | 21 ++++++++++++++++++---
queue_job/models/queue_job.py | 2 ++
queue_job/views/queue_job_views.xml | 22 ++++++++++++++--------
test_queue_job/tests/test_job.py | 12 +++++++++---
5 files changed, 60 insertions(+), 17 deletions(-)
diff --git a/queue_job/controllers/main.py b/queue_job/controllers/main.py
index 4c3d30b596..1b35c1ac99 100644
--- a/queue_job/controllers/main.py
+++ b/queue_job/controllers/main.py
@@ -100,15 +100,17 @@ def retry_postpone(job, message, seconds=None):
# retries are exhausted
env.cr.rollback()
- except (FailedJobError, Exception):
+ except (FailedJobError, Exception) as orig_exception:
buff = StringIO()
traceback.print_exc(file=buff)
- _logger.error(buff.getvalue())
+ traceback_txt = buff.getvalue()
+ _logger.error(traceback_txt)
job.env.clear()
with odoo.api.Environment.manage():
with odoo.registry(job.env.cr.dbname).cursor() as new_cr:
job.env = job.env(cr=new_cr)
- job.set_failed(exc_info=buff.getvalue())
+ vals = self._get_failure_values(job, traceback_txt, orig_exception)
+ job.set_failed(**vals)
job.store()
new_cr.commit()
buff.close()
@@ -116,6 +118,18 @@ def retry_postpone(job, message, seconds=None):
return ""
+ def _get_failure_values(self, job, traceback_txt, orig_exception):
+ """Collect relevant data from exception."""
+ exception_name = orig_exception.__class__.__name__
+ if hasattr(orig_exception, "__module__"):
+ exception_name = orig_exception.__module__ + "." + exception_name
+ exc_message = getattr(orig_exception, "name", str(orig_exception))
+ return {
+ "exc_info": traceback_txt,
+ "exc_name": exception_name,
+ "exc_message": exc_message,
+ }
+
@http.route("/queue_job/create_test_job", type="http", auth="user")
def create_test_job(
self, priority=None, max_retries=None, channel=None, description="Test job"
diff --git a/queue_job/job.py b/queue_job/job.py
index eda0827193..287ab701db 100644
--- a/queue_job/job.py
+++ b/queue_job/job.py
@@ -214,6 +214,14 @@ class Job(object):
A description of the result (for humans).
+ .. attribute:: exc_name
+
+ Exception error name when the job failed.
+
+ .. attribute:: exc_message
+
+ Exception error message when the job failed.
+
.. attribute:: exc_info
Exception information (traceback) when the job failed.
@@ -478,6 +486,8 @@ def __init__(
self.date_done = None
self.result = None
+ self.exc_name = None
+ self.exc_message = None
self.exc_info = None
if "company_id" in env.context:
@@ -523,6 +533,8 @@ def store(self):
"priority": self.priority,
"retry": self.retry,
"max_retries": self.max_retries,
+ "exc_name": self.exc_name,
+ "exc_message": self.exc_message,
"exc_info": self.exc_info,
"company_id": self.company_id,
"result": str(self.result) if self.result else False,
@@ -700,15 +712,17 @@ def set_started(self):
def set_done(self, result=None):
self.state = DONE
+ self.exc_name = None
self.exc_info = None
self.date_done = datetime.now()
if result is not None:
self.result = result
- def set_failed(self, exc_info=None):
+ def set_failed(self, **kw):
self.state = FAILED
- if exc_info is not None:
- self.exc_info = exc_info
+ for k, v in kw.items():
+ if v is not None:
+ setattr(self, k, v)
def __repr__(self):
return "" % (self.uuid, self.priority)
@@ -739,6 +753,7 @@ def postpone(self, result=None, seconds=None):
"""
eta_seconds = self._get_retry_seconds(seconds)
self.eta = timedelta(seconds=eta_seconds)
+ self.exc_name = None
self.exc_info = None
if result is not None:
self.result = result
diff --git a/queue_job/models/queue_job.py b/queue_job/models/queue_job.py
index 7a1992972a..23d12abb28 100644
--- a/queue_job/models/queue_job.py
+++ b/queue_job/models/queue_job.py
@@ -68,6 +68,8 @@ class QueueJob(models.Model):
state = fields.Selection(STATES, readonly=True, required=True, index=True)
priority = fields.Integer()
+ exc_name = fields.Char(string="Exception", readonly=True)
+ exc_message = fields.Char(string="Exception Message", readonly=True)
exc_info = fields.Text(string="Exception Info", readonly=True)
result = fields.Text(readonly=True)
diff --git a/queue_job/views/queue_job_views.xml b/queue_job/views/queue_job_views.xml
index 59d2fa01df..e5f7991593 100644
--- a/queue_job/views/queue_job_views.xml
+++ b/queue_job/views/queue_job_views.xml
@@ -71,6 +71,18 @@
> If the max. retries is 0, the number of retries is infinite.
+
+
+
+
+
+
+
-
-
-
@@ -108,10 +113,11 @@
-
+
+
diff --git a/test_queue_job/tests/test_job.py b/test_queue_job/tests/test_job.py
index 9eef6d2195..5399d795b5 100644
--- a/test_queue_job/tests/test_job.py
+++ b/test_queue_job/tests/test_job.py
@@ -163,9 +163,15 @@ def test_set_done(self):
def test_set_failed(self):
job_a = Job(self.method)
- job_a.set_failed(exc_info="failed test")
- self.assertEqual(job_a.state, FAILED)
- self.assertEqual(job_a.exc_info, "failed test")
+ job_a.set_failed(
+ exc_info="failed test",
+ exc_name="FailedTest",
+ exc_message="Sadly this job failed",
+ )
+ self.assertEquals(job_a.state, FAILED)
+ self.assertEquals(job_a.exc_info, "failed test")
+ self.assertEquals(job_a.exc_name, "FailedTest")
+ self.assertEquals(job_a.exc_message, "Sadly this job failed")
def test_postpone(self):
job_a = Job(self.method)
From 5607bd7886780016e671252cafdd8179de2fba38 Mon Sep 17 00:00:00 2001
From: Simone Orsi
Date: Mon, 29 Mar 2021 09:15:36 +0200
Subject: [PATCH 093/451] queue_job: improve filtering and grouping
---
queue_job/views/queue_job_views.xml | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/queue_job/views/queue_job_views.xml b/queue_job/views/queue_job_views.xml
index e5f7991593..fd6f3e0bba 100644
--- a/queue_job/views/queue_job_views.xml
+++ b/queue_job/views/queue_job_views.xml
@@ -118,6 +118,7 @@
+
@@ -159,6 +160,10 @@
+
+
+
+
+
+
From 6c1de75d753d215a6f08e5474537cbf73aee8a4a Mon Sep 17 00:00:00 2001
From: Simone Orsi
Date: Mon, 29 Mar 2021 10:06:32 +0200
Subject: [PATCH 094/451] queue_job: add hook to customize stored values
---
queue_job/job.py | 45 +++++++++++++++++++++-------
queue_job/models/base.py | 18 ++++++++++-
test_queue_job/models/test_models.py | 8 +++++
test_queue_job/tests/test_job.py | 10 +++++++
4 files changed, 70 insertions(+), 11 deletions(-)
diff --git a/queue_job/job.py b/queue_job/job.py
index 287ab701db..a4dc224b85 100644
--- a/queue_job/job.py
+++ b/queue_job/job.py
@@ -528,6 +528,22 @@ def perform(self):
def store(self):
"""Store the Job"""
+ job_model = self.env["queue.job"]
+ # The sentinel is used to prevent edition sensitive fields (such as
+ # method_name) from RPC methods.
+ edit_sentinel = job_model.EDIT_SENTINEL
+
+ db_record = self.db_record()
+ if db_record:
+ db_record.with_context(_job_edit_sentinel=edit_sentinel).write(
+ self._store_values()
+ )
+ else:
+ job_model.with_context(_job_edit_sentinel=edit_sentinel).sudo().create(
+ self._store_values(create=True)
+ )
+
+ def _store_values(self, create=False):
vals = {
"state": self.state,
"priority": self.priority,
@@ -560,15 +576,7 @@ def store(self):
if self.identity_key:
vals["identity_key"] = self.identity_key
- job_model = self.env["queue.job"]
- # The sentinel is used to prevent edition sensitive fields (such as
- # method_name) from RPC methods.
- edit_sentinel = job_model.EDIT_SENTINEL
-
- db_record = self.db_record()
- if db_record:
- db_record.with_context(_job_edit_sentinel=edit_sentinel).write(vals)
- else:
+ if create:
vals.update(
{
"user_id": self.env.uid,
@@ -588,7 +596,24 @@ def store(self):
"kwargs": self.kwargs,
}
)
- job_model.with_context(_job_edit_sentinel=edit_sentinel).sudo().create(vals)
+
+ vals_from_model = self._store_values_from_model()
+ # Sanitize values: make sure you cannot screw core values
+ vals_from_model = {k: v for k, v in vals_from_model.items() if k not in vals}
+ vals.update(vals_from_model)
+ return vals
+
+ def _store_values_from_model(self):
+ vals = {}
+ value_handlers_candidates = (
+ "_job_store_values_for_" + self.method_name,
+ "_job_store_values",
+ )
+ for candidate in value_handlers_candidates:
+ handler = getattr(self.recordset, candidate, None)
+ if handler is not None:
+ vals = handler(self)
+ return vals
@property
def func_string(self):
diff --git a/queue_job/models/base.py b/queue_job/models/base.py
index 5b1f8fee79..a398bf85fb 100644
--- a/queue_job/models/base.py
+++ b/queue_job/models/base.py
@@ -5,7 +5,7 @@
import logging
import os
-from odoo import models
+from odoo import api, models
from ..job import DelayableRecordset
@@ -189,3 +189,19 @@ def auto_delay_wrapper(self, *args, **kwargs):
origin = getattr(self, method_name)
return functools.update_wrapper(auto_delay_wrapper, origin)
+
+ @api.model
+ def _job_store_values(self, job):
+ """Hook for manipulating job stored values.
+
+ You can define a more specific hook for a job function
+ by defining a method name with this pattern:
+
+ `_queue_job_store_values_${func_name}`
+
+ NOTE: values will be stored only if they match stored fields on `queue.job`.
+
+ :param job: current queue_job.job.Job instance.
+ :return: dictionary for setting job values.
+ """
+ return {}
diff --git a/test_queue_job/models/test_models.py b/test_queue_job/models/test_models.py
index a5a3843230..a9f32dec0e 100644
--- a/test_queue_job/models/test_models.py
+++ b/test_queue_job/models/test_models.py
@@ -10,6 +10,8 @@ class QueueJob(models.Model):
_inherit = "queue.job"
+ additional_info = fields.Char()
+
def testing_related_method(self, **kwargs):
return self, kwargs
@@ -88,6 +90,12 @@ def _register_hook(self):
)
return super()._register_hook()
+ def _job_store_values(self, job):
+ value = "JUST_TESTING"
+ if job.state == "failed":
+ value += "_BUT_FAILED"
+ return {"additional_info": value}
+
class TestQueueChannel(models.Model):
diff --git a/test_queue_job/tests/test_job.py b/test_queue_job/tests/test_job.py
index 5399d795b5..c78c551f2a 100644
--- a/test_queue_job/tests/test_job.py
+++ b/test_queue_job/tests/test_job.py
@@ -233,6 +233,16 @@ def test_store(self):
stored = self.queue_job.search([("uuid", "=", test_job.uuid)])
self.assertEqual(len(stored), 1)
+ def test_store_extra_data(self):
+ test_job = Job(self.method)
+ test_job.store()
+ stored = self.queue_job.search([("uuid", "=", test_job.uuid)])
+ self.assertEqual(stored.additional_info, "JUST_TESTING")
+ test_job.set_failed(exc_info="failed test", exc_name="FailedTest")
+ test_job.store()
+ stored.invalidate_cache()
+ self.assertEqual(stored.additional_info, "JUST_TESTING_BUT_FAILED")
+
def test_read(self):
eta = datetime.now() + timedelta(hours=5)
test_job = Job(
From 4b8781c733e75de9dfe5f25e4629f9d76cafd566 Mon Sep 17 00:00:00 2001
From: Simone Orsi
Date: Mon, 29 Mar 2021 11:34:30 +0200
Subject: [PATCH 095/451] queue_job: migration step to store exception data
---
.../migrations/13.0.3.8.0/post-migration.py | 47 +++++++++++++++++++
1 file changed, 47 insertions(+)
create mode 100644 queue_job/migrations/13.0.3.8.0/post-migration.py
diff --git a/queue_job/migrations/13.0.3.8.0/post-migration.py b/queue_job/migrations/13.0.3.8.0/post-migration.py
new file mode 100644
index 0000000000..f6eff72707
--- /dev/null
+++ b/queue_job/migrations/13.0.3.8.0/post-migration.py
@@ -0,0 +1,47 @@
+# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
+
+import logging
+
+from odoo import SUPERUSER_ID, api
+
+_logger = logging.getLogger(__name__)
+
+
+def migrate(cr, version):
+ with api.Environment.manage():
+ env = api.Environment(cr, SUPERUSER_ID, {})
+ _logger.info("Computing exception name for failed jobs")
+ _compute_jobs_new_values(env)
+
+
+def _compute_jobs_new_values(env):
+ for job in env["queue.job"].search(
+ [("state", "=", "failed"), ("exc_info", "!=", False)]
+ ):
+ exception_details = _get_exception_details(job)
+ if exception_details:
+ job.update(exception_details)
+
+
+def _get_exception_details(job):
+ for line in reversed(job.exc_info.splitlines()):
+ if _find_exception(line):
+ name, msg = line.split(":", 1)
+ return {
+ "exc_name": name.strip(),
+ "exc_message": msg.strip("()', \""),
+ }
+
+
+def _find_exception(line):
+ # Just a list of common errors.
+ # If you want to target others, add your own migration step for your db.
+ exceptions = (
+ "Error:", # catch all well named exceptions
+ # other live instance errors found
+ "requests.exceptions.MissingSchema",
+ "botocore.errorfactory.NoSuchKey",
+ )
+ for exc in exceptions:
+ if exc in line:
+ return exc
From 192cc652d802da2d8c597ccc0fb845f58d742915 Mon Sep 17 00:00:00 2001
From: Guewen Baconnier
Date: Mon, 31 May 2021 21:34:19 +0200
Subject: [PATCH 096/451] Fix display on exec_time on tree view as seconds
The float_time widget shows hours seconds, we only want seconds.
The widget had been removed on the form view, but not on the tree view.
---
queue_job/views/queue_job_views.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/queue_job/views/queue_job_views.xml b/queue_job/views/queue_job_views.xml
index fd6f3e0bba..cb7aeac28b 100644
--- a/queue_job/views/queue_job_views.xml
+++ b/queue_job/views/queue_job_views.xml
@@ -116,7 +116,7 @@
-
+
From fc484964040d283c4bbe791e33117cd242b70ed4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C5=A9ng=20=28Tr=E1=BA=A7n=20=C4=90=C3=ACnh=29?=
Date: Tue, 23 Nov 2021 18:24:18 +0700
Subject: [PATCH 097/451] [IMP] queue_job: black, isort, prettier
---
queue_job/models/queue_job.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/queue_job/models/queue_job.py b/queue_job/models/queue_job.py
index 23d12abb28..2d8ef74538 100644
--- a/queue_job/models/queue_job.py
+++ b/queue_job/models/queue_job.py
@@ -94,7 +94,9 @@ class QueueJob(models.Model):
# FIXME the name of this field is very confusing
channel_method_name = fields.Char(readonly=True)
job_function_id = fields.Many2one(
- comodel_name="queue.job.function", string="Job Function", readonly=True,
+ comodel_name="queue.job.function",
+ string="Job Function",
+ readonly=True,
)
channel = fields.Char(index=True)
From 3051240d5da37fb523aeaf27fa50de8eafb0f322 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C5=A9ng=20=28Tr=E1=BA=A7n=20=C4=90=C3=ACnh=29?=
Date: Tue, 23 Nov 2021 19:37:47 +0700
Subject: [PATCH 098/451] Forward migration scripts from #309 #328
---
queue_job/__manifest__.py | 2 +-
.../migrations/{13.0.3.8.0 => 14.0.1.4.0}/post-migration.py | 0
.../migrations/{13.0.3.7.0 => 14.0.1.4.0}/pre-migration.py | 4 ----
3 files changed, 1 insertion(+), 5 deletions(-)
rename queue_job/migrations/{13.0.3.8.0 => 14.0.1.4.0}/post-migration.py (100%)
rename queue_job/migrations/{13.0.3.7.0 => 14.0.1.4.0}/pre-migration.py (93%)
diff --git a/queue_job/__manifest__.py b/queue_job/__manifest__.py
index 2e5a2ef610..d100873272 100644
--- a/queue_job/__manifest__.py
+++ b/queue_job/__manifest__.py
@@ -3,7 +3,7 @@
{
"name": "Job Queue",
- "version": "14.0.1.3.4",
+ "version": "14.0.1.4.0",
"author": "Camptocamp,ACSONE SA/NV,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/queue",
"license": "LGPL-3",
diff --git a/queue_job/migrations/13.0.3.8.0/post-migration.py b/queue_job/migrations/14.0.1.4.0/post-migration.py
similarity index 100%
rename from queue_job/migrations/13.0.3.8.0/post-migration.py
rename to queue_job/migrations/14.0.1.4.0/post-migration.py
diff --git a/queue_job/migrations/13.0.3.7.0/pre-migration.py b/queue_job/migrations/14.0.1.4.0/pre-migration.py
similarity index 93%
rename from queue_job/migrations/13.0.3.7.0/pre-migration.py
rename to queue_job/migrations/14.0.1.4.0/pre-migration.py
index c14d6800ad..7b15de8e58 100644
--- a/queue_job/migrations/13.0.3.7.0/pre-migration.py
+++ b/queue_job/migrations/14.0.1.4.0/pre-migration.py
@@ -1,11 +1,7 @@
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
-import logging
-
from odoo.tools.sql import column_exists
-_logger = logging.getLogger(__name__)
-
def migrate(cr, version):
if not column_exists(cr, "queue_job", "exec_time"):
From fb925cb38aa1f916ac9ca0b0983705ce35624123 Mon Sep 17 00:00:00 2001
From: Enric Tobella
Date: Tue, 22 Jun 2021 15:11:13 +0200
Subject: [PATCH 099/451] [FIX] queue_job: Migrations raising errors with
OpenUpgrade
---
queue_job/migrations/14.0.1.4.0/pre-migration.py | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/queue_job/migrations/14.0.1.4.0/pre-migration.py b/queue_job/migrations/14.0.1.4.0/pre-migration.py
index 7b15de8e58..8ae6cb3a5f 100644
--- a/queue_job/migrations/14.0.1.4.0/pre-migration.py
+++ b/queue_job/migrations/14.0.1.4.0/pre-migration.py
@@ -1,10 +1,12 @@
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
-from odoo.tools.sql import column_exists
+from odoo.tools.sql import column_exists, table_exists
def migrate(cr, version):
- if not column_exists(cr, "queue_job", "exec_time"):
+ if table_exists(cr, "queue_job") and not column_exists(
+ cr, "queue_job", "exec_time"
+ ):
# Disable trigger otherwise the update takes ages.
cr.execute(
"""
From 6d8e7b3cff7d14547a956bee8a2d9790c89d4649 Mon Sep 17 00:00:00 2001
From: oca-travis
Date: Wed, 1 Dec 2021 07:49:19 +0000
Subject: [PATCH 100/451] [UPD] Update queue_job.pot
---
queue_job/i18n/queue_job.pot | 46 +++++++++++++++++++++++++++++++-----
1 file changed, 40 insertions(+), 6 deletions(-)
diff --git a/queue_job/i18n/queue_job.pot b/queue_job/i18n/queue_job.pot
index 9dca90c40a..e495d6191c 100644
--- a/queue_job/i18n/queue_job.pot
+++ b/queue_job/i18n/queue_job.pot
@@ -194,6 +194,12 @@ msgstr ""
msgid "Enqueued"
msgstr ""
+#. module: queue_job
+#: model:ir.model.fields,field_description:queue_job.field_queue_job__exc_name
+#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search
+msgid "Exception"
+msgstr ""
+
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job__exc_info
msgid "Exception Info"
@@ -204,11 +210,31 @@ msgstr ""
msgid "Exception Information"
msgstr ""
+#. module: queue_job
+#: model:ir.model.fields,field_description:queue_job.field_queue_job__exc_message
+msgid "Exception Message"
+msgstr ""
+
+#. module: queue_job
+#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search
+msgid "Exception message"
+msgstr ""
+
+#. module: queue_job
+#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form
+msgid "Exception:"
+msgstr ""
+
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job__eta
msgid "Execute only after"
msgstr ""
+#. module: queue_job
+#: model:ir.model.fields,field_description:queue_job.field_queue_job__exec_time
+msgid "Execution Time (avg)"
+msgstr ""
+
#. module: queue_job
#: model:ir.model.fields.selection,name:queue_job.selection__queue_job__state__failed
#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search
@@ -359,6 +385,8 @@ msgstr ""
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__job_ids
#: model:ir.ui.menu,name:queue_job.menu_queue_job
#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form
+#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_graph
+#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_pivot
#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search
msgid "Jobs"
msgstr ""
@@ -438,6 +466,7 @@ msgstr ""
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job__model_name
#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__model_id
+#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search
msgid "Model"
msgstr ""
@@ -510,11 +539,6 @@ msgstr ""
msgid "Number of unread messages"
msgstr ""
-#. module: queue_job
-#: model:ir.model.fields,field_description:queue_job.field_queue_job__override_channel
-msgid "Override Channel"
-msgstr ""
-
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__parent_id
msgid "Parent Channel"
@@ -559,7 +583,7 @@ msgstr ""
#. module: queue_job
#: code:addons/queue_job/models/queue_job.py:0
#, python-format
-msgid "Queue jobs must created by calling 'with_delay()'."
+msgid "Queue jobs must be created by calling 'with_delay()'."
msgstr ""
#. module: queue_job
@@ -734,6 +758,16 @@ msgstr ""
msgid "The selected jobs will be set to done."
msgstr ""
+#. module: queue_job
+#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form
+msgid "Time (s)"
+msgstr ""
+
+#. module: queue_job
+#: model:ir.model.fields,help:queue_job.field_queue_job__exec_time
+msgid "Time required to execute this job in seconds. Average when grouped."
+msgstr ""
+
#. module: queue_job
#: model:ir.model.fields,help:queue_job.field_queue_job__activity_exception_decoration
msgid "Type of the exception activity on record."
From ee87acc8c402a28178edfd90bfc70a532684778f Mon Sep 17 00:00:00 2001
From: oca-travis
Date: Wed, 1 Dec 2021 07:49:21 +0000
Subject: [PATCH 101/451] [UPD] Update test_queue_job.pot
---
test_queue_job/i18n/test_queue_job.pot | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/test_queue_job/i18n/test_queue_job.pot b/test_queue_job/i18n/test_queue_job.pot
index 5edefd8e41..c8c1f66198 100644
--- a/test_queue_job/i18n/test_queue_job.pot
+++ b/test_queue_job/i18n/test_queue_job.pot
@@ -13,6 +13,11 @@ msgstr ""
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
+#. module: test_queue_job
+#: model:ir.model.fields,field_description:test_queue_job.field_queue_job__additional_info
+msgid "Additional Info"
+msgstr ""
+
#. module: test_queue_job
#: model:ir.model.fields,field_description:test_queue_job.field_test_queue_channel__create_uid
#: model:ir.model.fields,field_description:test_queue_job.field_test_queue_job__create_uid
From 832582f377c625d8ff8a8cf0bc6a1f5b1d30c934 Mon Sep 17 00:00:00 2001
From: OCA-git-bot
Date: Wed, 1 Dec 2021 08:00:15 +0000
Subject: [PATCH 102/451] [UPD] addons table in README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 72ebcd305a..8c423914b6 100644
--- a/README.md
+++ b/README.md
@@ -20,7 +20,7 @@ Available addons
addon | version | maintainers | summary
--- | --- | --- | ---
[base_import_async](base_import_async/) | 14.0.1.0.1 | | Import CSV files in the background
-[queue_job](queue_job/) | 14.0.1.3.4 | [](https://github.com/guewen) | Job Queue
+[queue_job](queue_job/) | 14.0.1.4.0 | [](https://github.com/guewen) | Job Queue
[queue_job_cron](queue_job_cron/) | 14.0.1.1.0 | | Scheduled Actions as Queue Jobs
[queue_job_subscribe](queue_job_subscribe/) | 14.0.1.0.0 | | Control which users are subscribed to queue job notifications
[test_base_import_async](test_base_import_async/) | 14.0.1.0.1 | | Test suite for base_import_async. Normally you don't need to install this.
From 774337092bcf2f8c74003062e2c74a0246e4f47c Mon Sep 17 00:00:00 2001
From: OCA-git-bot
Date: Wed, 1 Dec 2021 08:00:17 +0000
Subject: [PATCH 103/451] test_queue_job 14.0.2.0.0
---
test_queue_job/__manifest__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test_queue_job/__manifest__.py b/test_queue_job/__manifest__.py
index 094d37d763..0947b7df37 100644
--- a/test_queue_job/__manifest__.py
+++ b/test_queue_job/__manifest__.py
@@ -3,7 +3,7 @@
{
"name": "Queue Job Tests",
- "version": "14.0.1.3.1",
+ "version": "14.0.2.0.0",
"author": "Camptocamp,Odoo Community Association (OCA)",
"license": "LGPL-3",
"category": "Generic Modules",
From 636fe133a8169fb7debefa24b0735a3d9f07c5d0 Mon Sep 17 00:00:00 2001
From: OCA-git-bot
Date: Wed, 1 Dec 2021 08:00:18 +0000
Subject: [PATCH 104/451] queue_job 14.0.2.0.0
---
queue_job/__manifest__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/queue_job/__manifest__.py b/queue_job/__manifest__.py
index d100873272..a202e87b46 100644
--- a/queue_job/__manifest__.py
+++ b/queue_job/__manifest__.py
@@ -3,7 +3,7 @@
{
"name": "Job Queue",
- "version": "14.0.1.4.0",
+ "version": "14.0.2.0.0",
"author": "Camptocamp,ACSONE SA/NV,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/queue",
"license": "LGPL-3",
From 9530c4c1bbd78ca620c132ed3e6a7c61f792cbb4 Mon Sep 17 00:00:00 2001
From: OCA-git-bot
Date: Wed, 1 Dec 2021 08:00:22 +0000
Subject: [PATCH 105/451] [UPD] addons table in README.md
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 8c423914b6..3f3c7fb42c 100644
--- a/README.md
+++ b/README.md
@@ -20,11 +20,11 @@ Available addons
addon | version | maintainers | summary
--- | --- | --- | ---
[base_import_async](base_import_async/) | 14.0.1.0.1 | | Import CSV files in the background
-[queue_job](queue_job/) | 14.0.1.4.0 | [](https://github.com/guewen) | Job Queue
+[queue_job](queue_job/) | 14.0.2.0.0 | [](https://github.com/guewen) | Job Queue
[queue_job_cron](queue_job_cron/) | 14.0.1.1.0 | | Scheduled Actions as Queue Jobs
[queue_job_subscribe](queue_job_subscribe/) | 14.0.1.0.0 | | Control which users are subscribed to queue job notifications
[test_base_import_async](test_base_import_async/) | 14.0.1.0.1 | | Test suite for base_import_async. Normally you don't need to install this.
-[test_queue_job](test_queue_job/) | 14.0.1.3.1 | | Queue Job Tests
+[test_queue_job](test_queue_job/) | 14.0.2.0.0 | | Queue Job Tests
Unported addons
From 29302244d802b99e6b145d4988d5a69504f4fe57 Mon Sep 17 00:00:00 2001
From: OCA Transbot
Date: Wed, 1 Dec 2021 08:00:31 +0000
Subject: [PATCH 106/451] Update translation files
Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.
Translation: queue-14.0/queue-14.0-queue_job
Translate-URL: https://translation.odoo-community.org/projects/queue-14-0/queue-14-0-queue_job/
---
queue_job/i18n/de.po | 49 ++++++++++++++++++++++++++++++++++++-----
queue_job/i18n/zh_CN.po | 49 ++++++++++++++++++++++++++++++++++++-----
2 files changed, 86 insertions(+), 12 deletions(-)
diff --git a/queue_job/i18n/de.po b/queue_job/i18n/de.po
index a6b84000ed..946045df0f 100644
--- a/queue_job/i18n/de.po
+++ b/queue_job/i18n/de.po
@@ -200,6 +200,12 @@ msgstr "Zeit der Einreihung"
msgid "Enqueued"
msgstr "Eingereiht"
+#. module: queue_job
+#: model:ir.model.fields,field_description:queue_job.field_queue_job__exc_name
+#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search
+msgid "Exception"
+msgstr ""
+
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job__exc_info
msgid "Exception Info"
@@ -210,11 +216,31 @@ msgstr "Exception-Info"
msgid "Exception Information"
msgstr "Exception-Information"
+#. module: queue_job
+#: model:ir.model.fields,field_description:queue_job.field_queue_job__exc_message
+msgid "Exception Message"
+msgstr ""
+
+#. module: queue_job
+#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search
+msgid "Exception message"
+msgstr ""
+
+#. module: queue_job
+#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form
+msgid "Exception:"
+msgstr ""
+
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job__eta
msgid "Execute only after"
msgstr "Erst ausführen nach"
+#. module: queue_job
+#: model:ir.model.fields,field_description:queue_job.field_queue_job__exec_time
+msgid "Execution Time (avg)"
+msgstr ""
+
#. module: queue_job
#: model:ir.model.fields.selection,name:queue_job.selection__queue_job__state__failed
#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search
@@ -367,6 +393,8 @@ msgstr "Job unterbrochen und als Erledigt markiert: Es ist nicht zu tun."
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__job_ids
#: model:ir.ui.menu,name:queue_job.menu_queue_job
#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form
+#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_graph
+#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_pivot
#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search
msgid "Jobs"
msgstr "Jobs"
@@ -447,6 +475,7 @@ msgstr "Methodenname"
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job__model_name
#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__model_id
+#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search
msgid "Model"
msgstr "Modell"
@@ -519,11 +548,6 @@ msgstr "Das ist die Anzahl von Nachrichten mit Übermittlungsfehler"
msgid "Number of unread messages"
msgstr "Das ist die Anzahl von ungelesenen Nachrichten"
-#. module: queue_job
-#: model:ir.model.fields,field_description:queue_job.field_queue_job__override_channel
-msgid "Override Channel"
-msgstr "Kanal überschreiben"
-
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__parent_id
msgid "Parent Channel"
@@ -571,7 +595,7 @@ msgstr "Job einreihen"
#. module: queue_job
#: code:addons/queue_job/models/queue_job.py:0
#, python-format
-msgid "Queue jobs must created by calling 'with_delay()'."
+msgid "Queue jobs must be created by calling 'with_delay()'."
msgstr ""
#. module: queue_job
@@ -760,6 +784,16 @@ msgstr "Die ausgewählten Jobs werden erneut eingereiht."
msgid "The selected jobs will be set to done."
msgstr "Die ausgewählten Jobs werden als Erledigt markiert."
+#. module: queue_job
+#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form
+msgid "Time (s)"
+msgstr ""
+
+#. module: queue_job
+#: model:ir.model.fields,help:queue_job.field_queue_job__exec_time
+msgid "Time required to execute this job in seconds. Average when grouped."
+msgstr ""
+
#. module: queue_job
#: model:ir.model.fields,help:queue_job.field_queue_job__activity_exception_decoration
msgid "Type of the exception activity on record."
@@ -824,6 +858,9 @@ msgstr "Assistent zur erneuten Einreihung einer Job-Auswahl"
msgid "Worker Pid"
msgstr ""
+#~ msgid "Override Channel"
+#~ msgstr "Kanal überschreiben"
+
#~ msgid "If checked new messages require your attention."
#~ msgstr ""
#~ "Wenn es gesetzt ist, erfordern neue Nachrichten Ihre Aufmerksamkeit."
diff --git a/queue_job/i18n/zh_CN.po b/queue_job/i18n/zh_CN.po
index 8470c9bbb9..255a4a8b61 100644
--- a/queue_job/i18n/zh_CN.po
+++ b/queue_job/i18n/zh_CN.po
@@ -199,6 +199,12 @@ msgstr "排队时间"
msgid "Enqueued"
msgstr "排队"
+#. module: queue_job
+#: model:ir.model.fields,field_description:queue_job.field_queue_job__exc_name
+#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search
+msgid "Exception"
+msgstr ""
+
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job__exc_info
msgid "Exception Info"
@@ -209,11 +215,31 @@ msgstr "异常信息"
msgid "Exception Information"
msgstr "异常信息"
+#. module: queue_job
+#: model:ir.model.fields,field_description:queue_job.field_queue_job__exc_message
+msgid "Exception Message"
+msgstr ""
+
+#. module: queue_job
+#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search
+msgid "Exception message"
+msgstr ""
+
+#. module: queue_job
+#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form
+msgid "Exception:"
+msgstr ""
+
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job__eta
msgid "Execute only after"
msgstr "仅在此之后执行"
+#. module: queue_job
+#: model:ir.model.fields,field_description:queue_job.field_queue_job__exec_time
+msgid "Execution Time (avg)"
+msgstr ""
+
#. module: queue_job
#: model:ir.model.fields.selection,name:queue_job.selection__queue_job__state__failed
#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search
@@ -365,6 +391,8 @@ msgstr "作业中断并设置为已完成:无需执行任何操作。"
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__job_ids
#: model:ir.ui.menu,name:queue_job.menu_queue_job
#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form
+#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_graph
+#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_pivot
#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search
msgid "Jobs"
msgstr "作业"
@@ -445,6 +473,7 @@ msgstr "方法名称"
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job__model_name
#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__model_id
+#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search
msgid "Model"
msgstr "模型"
@@ -517,11 +546,6 @@ msgstr "递送错误消息数量"
msgid "Number of unread messages"
msgstr "未读消息数量"
-#. module: queue_job
-#: model:ir.model.fields,field_description:queue_job.field_queue_job__override_channel
-msgid "Override Channel"
-msgstr "覆盖频道"
-
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__parent_id
msgid "Parent Channel"
@@ -569,7 +593,7 @@ msgstr "队列作业"
#. module: queue_job
#: code:addons/queue_job/models/queue_job.py:0
#, python-format
-msgid "Queue jobs must created by calling 'with_delay()'."
+msgid "Queue jobs must be created by calling 'with_delay()'."
msgstr ""
#. module: queue_job
@@ -755,6 +779,16 @@ msgstr "所选作业将重新排队。"
msgid "The selected jobs will be set to done."
msgstr "所选作业将设置为完成。"
+#. module: queue_job
+#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form
+msgid "Time (s)"
+msgstr ""
+
+#. module: queue_job
+#: model:ir.model.fields,help:queue_job.field_queue_job__exec_time
+msgid "Time required to execute this job in seconds. Average when grouped."
+msgstr ""
+
#. module: queue_job
#: model:ir.model.fields,help:queue_job.field_queue_job__activity_exception_decoration
msgid "Type of the exception activity on record."
@@ -819,6 +853,9 @@ msgstr "重新排队向导所选的作业"
msgid "Worker Pid"
msgstr ""
+#~ msgid "Override Channel"
+#~ msgstr "覆盖频道"
+
#~ msgid "If checked new messages require your attention."
#~ msgstr "查看是否有需要留意的新消息。"
From 92eae4b427578068c5d9256d51bf672938bc9422 Mon Sep 17 00:00:00 2001
From: OCA Transbot
Date: Wed, 1 Dec 2021 08:00:31 +0000
Subject: [PATCH 107/451] Update translation files
Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.
Translation: queue-14.0/queue-14.0-test_queue_job
Translate-URL: https://translation.odoo-community.org/projects/queue-14-0/queue-14-0-test_queue_job/
---
test_queue_job/i18n/zh_CN.po | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/test_queue_job/i18n/zh_CN.po b/test_queue_job/i18n/zh_CN.po
index 40fe2c8c9a..72587bfdf8 100644
--- a/test_queue_job/i18n/zh_CN.po
+++ b/test_queue_job/i18n/zh_CN.po
@@ -16,6 +16,11 @@ msgstr ""
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Weblate 3.8\n"
+#. module: test_queue_job
+#: model:ir.model.fields,field_description:test_queue_job.field_queue_job__additional_info
+msgid "Additional Info"
+msgstr ""
+
#. module: test_queue_job
#: model:ir.model.fields,field_description:test_queue_job.field_test_queue_channel__create_uid
#: model:ir.model.fields,field_description:test_queue_job.field_test_queue_job__create_uid
From 048cdf0ec23eae48d2cb6b16731d5024613d976c Mon Sep 17 00:00:00 2001
From: Enric Tobella
Date: Tue, 9 Jul 2019 09:42:36 +0200
Subject: [PATCH 108/451] [ADD] queue_job_batch
[UPD] Update queue_job_batch.pot
---
queue_job_batch/README.rst | 118 +++++
queue_job_batch/__init__.py | 1 +
queue_job_batch/__manifest__.py | 24 +
queue_job_batch/i18n/queue_job_batch.pot | 185 +++++++
queue_job_batch/models/__init__.py | 2 +
queue_job_batch/models/queue_job.py | 37 ++
queue_job_batch/models/queue_job_batch.py | 126 +++++
queue_job_batch/readme/CONTRIBUTORS.rst | 1 +
queue_job_batch/readme/DESCRIPTION.rst | 40 ++
queue_job_batch/readme/USAGE.rst | 2 +
queue_job_batch/security/ir.model.access.csv | 4 +
queue_job_batch/security/security.xml | 43 ++
queue_job_batch/static/description/icon.png | Bin 0 -> 1803 bytes
queue_job_batch/static/description/icon.svg | 127 +++++
queue_job_batch/static/description/index.html | 458 ++++++++++++++++++
queue_job_batch/static/src/js/systray.js | 139 ++++++
queue_job_batch/static/src/less/systray.less | 44 ++
queue_job_batch/static/src/xml/systray.xml | 62 +++
queue_job_batch/views/assets_backend.xml | 10 +
.../views/queue_job_batch_views.xml | 98 ++++
queue_job_batch/views/queue_job_views.xml | 37 ++
21 files changed, 1558 insertions(+)
create mode 100644 queue_job_batch/README.rst
create mode 100644 queue_job_batch/__init__.py
create mode 100644 queue_job_batch/__manifest__.py
create mode 100644 queue_job_batch/i18n/queue_job_batch.pot
create mode 100644 queue_job_batch/models/__init__.py
create mode 100644 queue_job_batch/models/queue_job.py
create mode 100644 queue_job_batch/models/queue_job_batch.py
create mode 100644 queue_job_batch/readme/CONTRIBUTORS.rst
create mode 100644 queue_job_batch/readme/DESCRIPTION.rst
create mode 100644 queue_job_batch/readme/USAGE.rst
create mode 100644 queue_job_batch/security/ir.model.access.csv
create mode 100644 queue_job_batch/security/security.xml
create mode 100644 queue_job_batch/static/description/icon.png
create mode 100644 queue_job_batch/static/description/icon.svg
create mode 100644 queue_job_batch/static/description/index.html
create mode 100644 queue_job_batch/static/src/js/systray.js
create mode 100644 queue_job_batch/static/src/less/systray.less
create mode 100644 queue_job_batch/static/src/xml/systray.xml
create mode 100644 queue_job_batch/views/assets_backend.xml
create mode 100644 queue_job_batch/views/queue_job_batch_views.xml
create mode 100644 queue_job_batch/views/queue_job_views.xml
diff --git a/queue_job_batch/README.rst b/queue_job_batch/README.rst
new file mode 100644
index 0000000000..0de1f6cadf
--- /dev/null
+++ b/queue_job_batch/README.rst
@@ -0,0 +1,118 @@
+===============
+Job Queue Batch
+===============
+
+.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! 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%2Fqueue-lightgray.png?logo=github
+ :target: https://github.com/OCA/queue/tree/11.0/queue_job_batch
+ :alt: OCA/queue
+.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
+ :target: https://translation.odoo-community.org/projects/queue-11-0/queue-11-0-queue_job_batch
+ :alt: Translate me on Weblate
+.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
+ :target: https://runbot.odoo-community.org/runbot/230/11.0
+ :alt: Try me on Runbot
+
+|badge1| |badge2| |badge3| |badge4| |badge5|
+
+This addon adds an a grouper for queue jobs.
+
+It allows to show your jobs in a batched form in order to know better the
+results.
+
+Example:
+
+.. code-block:: python
+
+ from odoo import models, fields, api
+ from odoo.addons.queue_job.job import job
+
+ class MyModel(models.Model):
+ _name = 'my.model'
+
+ @api.multi
+ @job
+ def my_method(self, a, k=None):
+ _logger.info('executed with a: %s and k: %s', a, k)
+
+
+ class MyOtherModel(models.Model):
+ _name = 'my.other.model'
+
+ @api.multi
+ def button_do_stuff(self):
+ batch = self.env['queue.job.batch'].get_new_batch('Group')
+ for i in range(1, 100):
+ self.env['my.model'].with_context(
+ job_batch=batch
+ ).with_delay().my_method('a', k=i)
+ batch.enqueue()
+
+
+In the snippet of code above, when we call ``button_do_stuff``, 100 jobs
+capturing the method and arguments will be postponed. It will be executed as
+soon as the Jobrunner has a free bucket, which can be instantaneous if no other
+job is running.
+
+Once all the jobs have finished, the grouper will be marked as finished.
+
+**Table of contents**
+
+.. contents::
+ :local:
+
+Usage
+=====
+
+You can manage your batch jobs from the Systray. A new button will be shown
+with your currently executing job batches and the recently finished job groups.
+
+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
+~~~~~~~
+
+* Creu Blanca
+
+Contributors
+~~~~~~~~~~~~
+
+* Enric Tobella
+
+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_batch/__init__.py b/queue_job_batch/__init__.py
new file mode 100644
index 0000000000..0650744f6b
--- /dev/null
+++ b/queue_job_batch/__init__.py
@@ -0,0 +1 @@
+from . import models
diff --git a/queue_job_batch/__manifest__.py b/queue_job_batch/__manifest__.py
new file mode 100644
index 0000000000..1c3facf102
--- /dev/null
+++ b/queue_job_batch/__manifest__.py
@@ -0,0 +1,24 @@
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
+
+
+{
+ 'name': 'Job Queue Batch',
+ 'version': '11.0.1.1.0',
+ 'author': 'Creu Blanca,Odoo Community Association (OCA)',
+ 'website': 'https://github.com/OCA/queue',
+ 'license': 'AGPL-3',
+ 'category': 'Generic Modules',
+ 'depends': [
+ 'queue_job',
+ ],
+ 'qweb': [
+ 'static/src/xml/systray.xml',
+ ],
+ 'data': [
+ 'security/security.xml',
+ 'security/ir.model.access.csv',
+ 'views/queue_job_views.xml',
+ 'views/queue_job_batch_views.xml',
+ 'views/assets_backend.xml',
+ ],
+}
diff --git a/queue_job_batch/i18n/queue_job_batch.pot b/queue_job_batch/i18n/queue_job_batch.pot
new file mode 100644
index 0000000000..7e24d85f5e
--- /dev/null
+++ b/queue_job_batch/i18n/queue_job_batch.pot
@@ -0,0 +1,185 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * queue_job_batch
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 11.0\n"
+"Report-Msgid-Bugs-To: \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: queue_job_batch
+#: model:ir.model,name:queue_job_batch.model_queue_job_batch
+msgid "Batch of jobs"
+msgstr ""
+
+#. module: queue_job_batch
+#: model:ir.model.fields,field_description:queue_job_batch.field_queue_job_batch_company_id
+msgid "Company"
+msgstr ""
+
+#. module: queue_job_batch
+#: model:ir.model.fields,field_description:queue_job_batch.field_queue_job_batch_completeness
+#: model:ir.ui.view,arch_db:queue_job_batch.view_queue_job_batch_form
+#: model:ir.ui.view,arch_db:queue_job_batch.view_queue_job_batch_tree
+msgid "Completeness"
+msgstr ""
+
+#. module: queue_job_batch
+#: model:ir.model.fields,field_description:queue_job_batch.field_queue_job_batch_display_name
+msgid "Display Name"
+msgstr ""
+
+#. module: queue_job_batch
+#: model:ir.ui.view,arch_db:queue_job_batch.view_queue_job_batch_search
+#: selection:queue.job.batch,state:0
+msgid "Draft"
+msgstr ""
+
+#. module: queue_job_batch
+#: selection:queue.job.batch,state:0
+msgid "Enqueued"
+msgstr ""
+
+#. module: queue_job_batch
+#: model:ir.model.fields,field_description:queue_job_batch.field_queue_job_batch_failed_job_count
+msgid "Failed Job Count"
+msgstr ""
+
+#. module: queue_job_batch
+#: model:ir.model.fields,field_description:queue_job_batch.field_queue_job_batch_failed_percentage
+msgid "Failed Percentage"
+msgstr ""
+
+#. module: queue_job_batch
+#: model:ir.ui.view,arch_db:queue_job_batch.view_queue_job_batch_search
+#: selection:queue.job.batch,state:0
+msgid "Finished"
+msgstr ""
+
+#. module: queue_job_batch
+#: model:ir.model.fields,field_description:queue_job_batch.field_queue_job_batch_finished_job_count
+msgid "Finished Job Count"
+msgstr ""
+
+#. module: queue_job_batch
+#: model:ir.ui.view,arch_db:queue_job_batch.view_queue_job_batch_search
+msgid "Group By"
+msgstr ""
+
+#. module: queue_job_batch
+#. openerp-web
+#: code:addons/queue_job_batch/static/src/xml/systray.xml:52
+#, python-format
+msgid "Hide"
+msgstr ""
+
+#. module: queue_job_batch
+#: model:ir.model.fields,field_description:queue_job_batch.field_queue_job_batch_id
+msgid "ID"
+msgstr ""
+
+#. module: queue_job_batch
+#: selection:queue.job.batch,state:0
+msgid "In Progress"
+msgstr ""
+
+#. module: queue_job_batch
+#: model:ir.ui.view,arch_db:queue_job_batch.view_queue_job_batch_search
+msgid "In progress"
+msgstr ""
+
+#. module: queue_job_batch
+#: model:ir.model.fields,field_description:queue_job_batch.field_queue_job_batch_is_read
+msgid "Is Read"
+msgstr ""
+
+#. module: queue_job_batch
+#: model:ir.model.fields,field_description:queue_job_batch.field_queue_job_batch_job_ids
+msgid "Job"
+msgstr ""
+
+#. module: queue_job_batch
+#: model:ir.model.fields,field_description:queue_job_batch.field_queue_job_job_batch_id
+#: model:ir.ui.view,arch_db:queue_job_batch.view_queue_job_batch_form
+msgid "Job Batch"
+msgstr ""
+
+#. module: queue_job_batch
+#: model:ir.ui.view,arch_db:queue_job_batch.view_queue_job_batch_tree
+msgid "Job Batches"
+msgstr ""
+
+#. module: queue_job_batch
+#: model:ir.model.fields,field_description:queue_job_batch.field_queue_job_batch_job_count
+msgid "Job Count"
+msgstr ""
+
+#. module: queue_job_batch
+#: model:res.groups,name:queue_job_batch.group_queue_job_batch_user
+msgid "Job Queue Batch User"
+msgstr ""
+
+#. module: queue_job_batch
+#. openerp-web
+#: code:addons/queue_job_batch/static/src/xml/systray.xml:7
+#: code:addons/queue_job_batch/static/src/xml/systray.xml:14
+#: model:ir.actions.act_window,name:queue_job_batch.action_queue_job_batch
+#: model:ir.actions.act_window,name:queue_job_batch.action_view_your_queue_job_batch
+#: model:ir.ui.menu,name:queue_job_batch.menu_queue_job_batch
+#, python-format
+msgid "Job batches"
+msgstr ""
+
+#. module: queue_job_batch
+#: model:ir.actions.act_window,name:queue_job_batch.action_queue_job_related
+#: model:ir.ui.view,arch_db:queue_job_batch.view_queue_job_batch_form
+msgid "Jobs"
+msgstr ""
+
+#. module: queue_job_batch
+#: model:ir.model.fields,field_description:queue_job_batch.field_queue_job_batch___last_update
+msgid "Last Modified on"
+msgstr ""
+
+#. module: queue_job_batch
+#: model:ir.model.fields,field_description:queue_job_batch.field_queue_job_batch_name
+msgid "Name"
+msgstr ""
+
+#. module: queue_job_batch
+#. openerp-web
+#: code:addons/queue_job_batch/static/src/xml/systray.xml:26
+#, python-format
+msgid "No jobs to view."
+msgstr ""
+
+#. module: queue_job_batch
+#: model:ir.model,name:queue_job_batch.model_queue_job
+msgid "Queue Job"
+msgstr ""
+
+#. module: queue_job_batch
+#: model:ir.model.fields,field_description:queue_job_batch.field_queue_job_batch_state
+#: model:ir.ui.view,arch_db:queue_job_batch.view_queue_job_batch_search
+msgid "State"
+msgstr ""
+
+#. module: queue_job_batch
+#: model:ir.model.fields,field_description:queue_job_batch.field_queue_job_batch_user_id
+#: model:ir.ui.view,arch_db:queue_job_batch.view_queue_job_batch_search
+msgid "User"
+msgstr ""
+
+#. module: queue_job_batch
+#. openerp-web
+#: code:addons/queue_job_batch/static/src/xml/systray.xml:16
+#, python-format
+msgid "View All"
+msgstr ""
+
diff --git a/queue_job_batch/models/__init__.py b/queue_job_batch/models/__init__.py
new file mode 100644
index 0000000000..f4cb3a5408
--- /dev/null
+++ b/queue_job_batch/models/__init__.py
@@ -0,0 +1,2 @@
+from . import queue_job
+from . import queue_job_batch
diff --git a/queue_job_batch/models/queue_job.py b/queue_job_batch/models/queue_job.py
new file mode 100644
index 0000000000..095ab006dd
--- /dev/null
+++ b/queue_job_batch/models/queue_job.py
@@ -0,0 +1,37 @@
+# Copyright 2019 Creu Blanca
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
+
+from odoo import models, fields, api
+
+
+class QueueJob(models.Model):
+ _inherit = 'queue.job'
+
+ job_batch_id = fields.Many2one(
+ 'queue.job.batch'
+ )
+
+ @api.model
+ def create(self, vals):
+ batch = self.env.context.get('job_batch')
+ if batch and isinstance(
+ batch, models.Model
+ ) and batch.state == 'draft':
+ vals.update({
+ 'job_batch_id': batch.id
+ })
+ return super().create(vals)
+
+ @api.multi
+ def write(self, vals):
+ batches = self.env['queue.job.batch']
+ for record in self:
+ if record.job_batch_id and record.state != 'done' and vals.get(
+ 'state', ''
+ ) == 'done':
+ batches |= record.job_batch_id
+ for batch in batches:
+ # We need to make it with delay in order to prevent two jobs
+ # to work with the same batch
+ batch.with_delay().check_state()
+ return super().write(vals)
diff --git a/queue_job_batch/models/queue_job_batch.py b/queue_job_batch/models/queue_job_batch.py
new file mode 100644
index 0000000000..ab4eb9e51d
--- /dev/null
+++ b/queue_job_batch/models/queue_job_batch.py
@@ -0,0 +1,126 @@
+# Copyright 2019 Creu Blanca
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
+
+from odoo import models, fields, api
+from odoo.addons.queue_job.job import job
+
+
+class QueueJobBatch(models.Model):
+ _name = 'queue.job.batch'
+ _inherit = ['mail.thread', 'mail.activity.mixin']
+ _description = 'Batch of jobs'
+ _log_access = False
+
+ name = fields.Char(
+ required=True,
+ readonly=True,
+ track_visibility='onchange',
+ )
+ job_ids = fields.One2many(
+ 'queue.job',
+ inverse_name='job_batch_id',
+ readonly=True,
+ )
+ job_count = fields.Integer(
+ compute='_compute_job_count',
+ )
+ user_id = fields.Many2one(
+ 'res.users',
+ required=True,
+ readonly=True,
+ track_visibility='onchange',
+ )
+ state = fields.Selection(
+ [
+ ('draft', 'Draft'),
+ ('enqueued', 'Enqueued'),
+ ('progress', 'In Progress'),
+ ('finished', 'Finished')
+ ],
+ default='draft',
+ required=True,
+ readonly=True,
+ track_visibility='onchange',
+ )
+ finished_job_count = fields.Float(
+ compute='_compute_job_count',
+ )
+ failed_job_count = fields.Float(
+ compute='_compute_job_count',
+ )
+ company_id = fields.Many2one(
+ 'res.company',
+ readonly=True,
+ )
+ is_read = fields.Boolean(
+ default=True
+ )
+ completeness = fields.Float(
+ compute='_compute_job_count',
+ )
+ failed_percentage = fields.Float(
+ compute='_compute_job_count',
+ )
+
+ def enqueue(self):
+ self.filtered(lambda r: r.state == 'draft').write({
+ 'state': 'enqueued'
+ })
+ for record in self:
+ record.check_state()
+
+ @job
+ def check_state(self):
+ self.ensure_one()
+ if self.state == 'enqueued' and any(
+ job.state not in ['pending', 'enqueued'] for job in self.job_ids
+ ):
+ self.write({'state': 'progress'})
+ if self.state != 'progress':
+ return True
+ if all(job.state == 'done' for job in self.job_ids):
+ self.write({
+ 'state': 'finished',
+ 'is_read': False,
+ })
+ return True
+
+ @api.multi
+ def set_read(self):
+ return self.write({'is_read': True})
+
+ @api.model
+ def get_new_batch(self, name, **kwargs):
+ vals = kwargs.copy()
+ if 'company_id' in self.env.context:
+ company_id = self.env.context['company_id']
+ else:
+ company_model = self.env['res.company']
+ company_model = company_model.sudo(self.env.uid)
+ company_id = company_model._company_default_get(
+ object='queue.job',
+ field='company_id'
+ ).id
+ vals.update({
+ 'user_id': self.env.uid,
+ 'name': name,
+ 'state': 'draft',
+ 'company_id': company_id,
+ })
+ return self.sudo().create(vals).sudo(self.env.uid)
+
+ @api.depends('job_ids')
+ def _compute_job_count(self):
+ for record in self:
+ job_count = len(record.job_ids)
+ failed_job_count = len(record.job_ids.filtered(
+ lambda r: r.state == 'failed'
+ ))
+ done_job_count = len(record.job_ids.filtered(
+ lambda r: r.state == 'done'
+ ))
+ record.job_count = job_count
+ record.finished_job_count = done_job_count
+ record.failed_job_count = failed_job_count
+ record.completeness = done_job_count / max(1, job_count)
+ record.failed_percentage = failed_job_count / max(1, job_count)
diff --git a/queue_job_batch/readme/CONTRIBUTORS.rst b/queue_job_batch/readme/CONTRIBUTORS.rst
new file mode 100644
index 0000000000..93ec993e04
--- /dev/null
+++ b/queue_job_batch/readme/CONTRIBUTORS.rst
@@ -0,0 +1 @@
+* Enric Tobella
diff --git a/queue_job_batch/readme/DESCRIPTION.rst b/queue_job_batch/readme/DESCRIPTION.rst
new file mode 100644
index 0000000000..a35df93da7
--- /dev/null
+++ b/queue_job_batch/readme/DESCRIPTION.rst
@@ -0,0 +1,40 @@
+This addon adds an a grouper for queue jobs.
+
+It allows to show your jobs in a batched form in order to know better the
+results.
+
+Example:
+
+.. code-block:: python
+
+ from odoo import models, fields, api
+ from odoo.addons.queue_job.job import job
+
+ class MyModel(models.Model):
+ _name = 'my.model'
+
+ @api.multi
+ @job
+ def my_method(self, a, k=None):
+ _logger.info('executed with a: %s and k: %s', a, k)
+
+
+ class MyOtherModel(models.Model):
+ _name = 'my.other.model'
+
+ @api.multi
+ def button_do_stuff(self):
+ batch = self.env['queue.job.batch'].get_new_batch('Group')
+ for i in range(1, 100):
+ self.env['my.model'].with_context(
+ job_batch=batch
+ ).with_delay().my_method('a', k=i)
+ batch.enqueue()
+
+
+In the snippet of code above, when we call ``button_do_stuff``, 100 jobs
+capturing the method and arguments will be postponed. It will be executed as
+soon as the Jobrunner has a free bucket, which can be instantaneous if no other
+job is running.
+
+Once all the jobs have finished, the grouper will be marked as finished.
diff --git a/queue_job_batch/readme/USAGE.rst b/queue_job_batch/readme/USAGE.rst
new file mode 100644
index 0000000000..4605405ae9
--- /dev/null
+++ b/queue_job_batch/readme/USAGE.rst
@@ -0,0 +1,2 @@
+You can manage your batch jobs from the Systray. A new button will be shown
+with your currently executing job batches and the recently finished job groups.
diff --git a/queue_job_batch/security/ir.model.access.csv b/queue_job_batch/security/ir.model.access.csv
new file mode 100644
index 0000000000..70d06ff3c6
--- /dev/null
+++ b/queue_job_batch/security/ir.model.access.csv
@@ -0,0 +1,4 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_queue_job_batch_user,queue job manager,model_queue_job_batch,group_queue_job_batch_user,1,0,0,0
+access_queue_job_batch_manager,queue job manager,model_queue_job_batch,queue_job.group_queue_job_manager,1,1,1,1
+access_queue_job_queue_job_batch_user,queue job manager,queue_job.model_queue_job,group_queue_job_batch_user,1,0,0,0
diff --git a/queue_job_batch/security/security.xml b/queue_job_batch/security/security.xml
new file mode 100644
index 0000000000..9205ca81bd
--- /dev/null
+++ b/queue_job_batch/security/security.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+ Job Queue Batch User
+
+
+
+
+
+
+
+
+
+
+ Job Queue batch multi-company
+
+
+ ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]
+
+
+
+ Job Queue batch user filter
+
+
+ [('user_id', '=', uid)]
+
+
+
+ Job Queue batch multi-company
+
+
+ [(1, '=', 1)]
+
+
+
+
+
diff --git a/queue_job_batch/static/description/icon.png b/queue_job_batch/static/description/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..a735e94d10274d83c86106188a9339a8132d3e0c
GIT binary patch
literal 1803
zcmah~c~H~W7XAgv(y&8>7!g9?2_uX6j9M0H2w{85B1;<{1rjv~0!hFHRF)XSs%&K|
zy8?01ZzMf}g@Olm>$+c8(L7t8l94@SXuM#Y%*o&QRbl=pI4m0ANqL(m=VhOB6+L
z*Lhdsd4F2?`FL_11&EK2w~3@iojXOQQ*3B)XUZjxC;)(DcoLig5-P-%UeO4O;j8M>
z5z5w&>``j7wRQDV!nGRcwLAY-?IhJ8X-03muA!FvvPmjPLLFW$74FebVjc)}q?aHH
zFett0P9zgO$<-1zeWEsVCA51utf&wyfBnP4
zYui_Ca`pMJWPQQIM)$SdG*dPyUpzv<+obbut06zJp(C9v+kFT$g+hKQwf|ReHT?yo
zHrWVM?x3A=>1bRjudwvQAYnd=b}QPoVOmDG@una4{ob^VXoI;sM^(+N)yy}cI-%^Y
zH4J+>PI%}?|Ig8Hv9V1lu)ynM0`rq9YWl|x`or_sR;%k;WFB@UtQ(P@9XxApZMiKo
z+b_{3xpAviio=LK&Eyevrz(1P$Eq7SVEB<^+~7Dn4m@%D2?y!`OnM_2$5XIJOuz8o^VI{hTb=VJrwRwQv(V^g40
zM?pu;)J55ClhJk;P?l=QVE^2sM1~3iyqU5ncT1n$f#}$n@pr<36201#pqWRIF|u>7
z7J02Kt>$kINfnF?DZE)hY`e|m>Gc+b-?v_)l(|CU{9;Is|*(t1cgRGs1ZNxCP^;VJCk
zfD4W?m^iEkD=w_T=&wXO>FC|16^18h{$H(mfRWFW`D=8`%2$M`v@8gB!pESar2=24~@
zY0LpD0VwMR;|@76rl@fl1osV4#cDL1(&@slA5H}jHzVjumaPn#08U|4H{v`Ay1B7l)atQ5H-JJEenHC24`
zlQ&RHRq71$t9}1t2!$_pd&Ec${+iT$xNYKEmT0o{wC?oMvxLval8<@^j%>H?W4V^WqaAQUGU`^1QF?3V7&bj)6;dWD0UNa*~U
zy7SGt4Q;N6fvI&Qkz#&8?F6}$&D!DeC+n2T+DS=!qT~--xC=5gWM^r76L9TpT;^zU
zp%#qA-g-riw9JpwQAX^#AY<^8KxI_)x8G>-A1xk>Lz^TW?yx@3!98mOy~Jp=7_w$F}>xp&gnbNY8kH_ee~;CT`T$B~s*QLN?5sOj{Q0bmRGdj$X{3Vamcw?nc&z(4hrqAcMB
zC}VpL1}Vq6`*?}6?t`l`1`aJ!oWShiDjU|YhyDP$9fEDW~l4_xhFwRfP9Cw*?0`4OzpVh;{rTgy$KKSB
+
+
+
diff --git a/queue_job_batch/static/description/index.html b/queue_job_batch/static/description/index.html
new file mode 100644
index 0000000000..47bdecb3c1
--- /dev/null
+++ b/queue_job_batch/static/description/index.html
@@ -0,0 +1,458 @@
+
+
+
+
+
+
+Job Queue Batch
+
+
+
+
+
Job Queue Batch
+
+
+
+
This addon adds an a grouper for queue jobs.
+
It allows to show your jobs in a batched form in order to know better the
+results.
In the snippet of code above, when we call button_do_stuff, 100 jobs
+capturing the method and arguments will be postponed. It will be executed as
+soon as the Jobrunner has a free bucket, which can be instantaneous if no other
+job is running.
+
Once all the jobs have finished, the grouper will be marked as finished.
You can manage your batch jobs from the Systray. A new button will be shown
+with your currently executing job batches and the recently finished job groups.
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.
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.
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.
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.
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.
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.
+
This module is part of the OCA/queue project on GitHub.
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.
+
+
diff --git a/setup/queue_job_context/odoo/addons/queue_job_context b/setup/queue_job_context/odoo/addons/queue_job_context
new file mode 120000
index 0000000000..d122d929fc
--- /dev/null
+++ b/setup/queue_job_context/odoo/addons/queue_job_context
@@ -0,0 +1 @@
+../../../../queue_job_context
\ No newline at end of file
diff --git a/setup/queue_job_context/setup.py b/setup/queue_job_context/setup.py
new file mode 100644
index 0000000000..28c57bb640
--- /dev/null
+++ b/setup/queue_job_context/setup.py
@@ -0,0 +1,6 @@
+import setuptools
+
+setuptools.setup(
+ setup_requires=['setuptools-odoo'],
+ odoo_addon=True,
+)
From 55b7f58f79f4263d39609757e256f9a80541bc17 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Bidoul?=
Date: Tue, 18 Oct 2022 13:01:20 +0200
Subject: [PATCH 237/451] Revert "[14.0][IMP] queue_job current company"
This reverts commit 309eb5fe55cb5c32ab4f075e619ccf81d46c74f1.
---
queue_job/job.py | 6 ------
1 file changed, 6 deletions(-)
diff --git a/queue_job/job.py b/queue_job/job.py
index ff95f9100a..457df97ecd 100644
--- a/queue_job/job.py
+++ b/queue_job/job.py
@@ -699,13 +699,7 @@ def db_record(self):
@property
def func(self):
- # We can fill only one company into allowed_company_ids.
- # Because if you have many, you can have unexpected records due to ir.rule.
- # ir.rule use allowed_company_ids to load every records in many companies.
- # But most of the time, a job should be executed on a single company.
recordset = self.recordset.with_context(job_uuid=self.uuid)
- if self.company_id:
- recordset = recordset.with_company(self.company_id)
return getattr(recordset, self.method_name)
@property
From 1ea2d3926e6372ebae2f01dc9e07a13d7191dc5d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Bidoul?=
Date: Tue, 18 Oct 2022 13:04:31 +0200
Subject: [PATCH 238/451] Update test with proper way to pass company to job
---
test_queue_job/models/test_models.py | 2 +-
test_queue_job/tests/test_job.py | 10 ++++------
2 files changed, 5 insertions(+), 7 deletions(-)
diff --git a/test_queue_job/models/test_models.py b/test_queue_job/models/test_models.py
index f810dba862..573e2380a9 100644
--- a/test_queue_job/models/test_models.py
+++ b/test_queue_job/models/test_models.py
@@ -40,7 +40,7 @@ class ModelTestQueueJob(models.Model):
# to test the context is serialized/deserialized properly
@api.model
def _job_prepare_context_before_enqueue_keys(self):
- return ("tz", "lang")
+ return ("tz", "lang", "allowed_company_ids")
def testing_method(self, *args, **kwargs):
"""Method used for tests
diff --git a/test_queue_job/tests/test_job.py b/test_queue_job/tests/test_job.py
index f8253db11a..46f75cc827 100644
--- a/test_queue_job/tests/test_job.py
+++ b/test_queue_job/tests/test_job.py
@@ -190,7 +190,7 @@ def test_company_simple(self):
company = self.env.ref("base.main_company")
eta = datetime.now() + timedelta(hours=5)
test_job = Job(
- self.method,
+ self.env["test.queue.job"].with_company(company).testing_method,
args=("o", "k"),
kwargs={"return_context": 1},
priority=15,
@@ -198,11 +198,10 @@ def test_company_simple(self):
description="My description",
)
test_job.worker_pid = 99999 # normally set on "set_start"
- test_job.company_id = company.id
test_job.store()
job_read = Job.load(self.env, test_job.uuid)
self.assertEqual(test_job.func.__func__, job_read.func.__func__)
- result_ctx = test_job.func(*tuple(test_job.args), **test_job.kwargs)
+ result_ctx = job_read.func(*tuple(test_job.args), **test_job.kwargs)
self.assertEqual(result_ctx.get("allowed_company_ids"), company.ids)
def test_company_complex(self):
@@ -214,7 +213,7 @@ def test_company_complex(self):
self.assertEqual(self.env.user.company_id, company1)
eta = datetime.now() + timedelta(hours=5)
test_job = Job(
- self.method,
+ self.env["test.queue.job"].with_company(company2).testing_method,
args=("o", "k"),
kwargs={"return_context": 1},
priority=15,
@@ -222,11 +221,10 @@ def test_company_complex(self):
description="My description",
)
test_job.worker_pid = 99999 # normally set on "set_start"
- test_job.company_id = company2.id
test_job.store()
job_read = Job.load(self.env, test_job.uuid)
self.assertEqual(test_job.func.__func__, job_read.func.__func__)
- result_ctx = test_job.func(*tuple(test_job.args), **test_job.kwargs)
+ result_ctx = job_read.func(*tuple(test_job.args), **test_job.kwargs)
self.assertEqual(result_ctx.get("allowed_company_ids"), company2.ids)
def test_store(self):
From 01b5dda82cb2bf0f92706d54144b06b75632085d Mon Sep 17 00:00:00 2001
From: OCA-git-bot
Date: Tue, 18 Oct 2022 17:57:41 +0000
Subject: [PATCH 239/451] test_queue_job 14.0.3.0.1
---
test_queue_job/__manifest__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test_queue_job/__manifest__.py b/test_queue_job/__manifest__.py
index f982061e4a..1891245041 100644
--- a/test_queue_job/__manifest__.py
+++ b/test_queue_job/__manifest__.py
@@ -3,7 +3,7 @@
{
"name": "Queue Job Tests",
- "version": "14.0.3.0.0",
+ "version": "14.0.3.0.1",
"author": "Camptocamp,Odoo Community Association (OCA)",
"license": "LGPL-3",
"category": "Generic Modules",
From 419ada0d4229e2a433eaf81c7d5a3c91f71078cb Mon Sep 17 00:00:00 2001
From: OCA-git-bot
Date: Tue, 18 Oct 2022 17:57:42 +0000
Subject: [PATCH 240/451] queue_job 14.0.3.0.6
---
queue_job/__manifest__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/queue_job/__manifest__.py b/queue_job/__manifest__.py
index 30e8fefebf..69739dc493 100644
--- a/queue_job/__manifest__.py
+++ b/queue_job/__manifest__.py
@@ -2,7 +2,7 @@
{
"name": "Job Queue",
- "version": "14.0.3.0.5",
+ "version": "14.0.3.0.6",
"author": "Camptocamp,ACSONE SA/NV,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/queue",
"license": "LGPL-3",
From 523621d216537d5424b063507dbbdae9ff2443e8 Mon Sep 17 00:00:00 2001
From: OCA-git-bot
Date: Tue, 18 Oct 2022 17:57:47 +0000
Subject: [PATCH 241/451] [UPD] addons table in README.md
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index d366c454f6..eab993aabb 100644
--- a/README.md
+++ b/README.md
@@ -22,12 +22,12 @@ Available addons
addon | version | maintainers | summary
--- | --- | --- | ---
[base_import_async](base_import_async/) | 14.0.1.0.1 | | Import CSV files in the background
-[queue_job](queue_job/) | 14.0.3.0.5 | [](https://github.com/guewen) | Job Queue
+[queue_job](queue_job/) | 14.0.3.0.6 | [](https://github.com/guewen) | Job Queue
[queue_job_batch](queue_job_batch/) | 14.0.1.0.0 | | Job Queue Batch
[queue_job_cron](queue_job_cron/) | 14.0.1.1.0 | | Scheduled Actions as Queue Jobs
[queue_job_subscribe](queue_job_subscribe/) | 14.0.1.0.0 | | Control which users are subscribed to queue job notifications
[test_base_import_async](test_base_import_async/) | 14.0.1.0.1 | | Test suite for base_import_async. Normally you don't need to install this.
-[test_queue_job](test_queue_job/) | 14.0.3.0.0 | | Queue Job Tests
+[test_queue_job](test_queue_job/) | 14.0.3.0.1 | | Queue Job Tests
[test_queue_job_batch](test_queue_job_batch/) | 14.0.1.0.0 | | Test Job Queue Batch
From 13429abfd1bc0e18e50c2926131a1bb430f7562c Mon Sep 17 00:00:00 2001
From: oca-ci
Date: Wed, 19 Oct 2022 15:00:26 +0000
Subject: [PATCH 242/451] [UPD] Update queue_job_context.pot
---
queue_job_context/i18n/queue_job_context.pot | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
create mode 100644 queue_job_context/i18n/queue_job_context.pot
diff --git a/queue_job_context/i18n/queue_job_context.pot b/queue_job_context/i18n/queue_job_context.pot
new file mode 100644
index 0000000000..0ad3f6e0dc
--- /dev/null
+++ b/queue_job_context/i18n/queue_job_context.pot
@@ -0,0 +1,19 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * queue_job_context
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 14.0\n"
+"Report-Msgid-Bugs-To: \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: queue_job_context
+#: model:ir.model,name:queue_job_context.model_base
+msgid "Base"
+msgstr ""
From c0df880ee02ef01bff40298fd708bbf64340832d Mon Sep 17 00:00:00 2001
From: OCA-git-bot
Date: Wed, 19 Oct 2022 15:04:15 +0000
Subject: [PATCH 243/451] [UPD] addons table in README.md
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index eab993aabb..4068ff4f86 100644
--- a/README.md
+++ b/README.md
@@ -24,6 +24,7 @@ addon | version | maintainers | summary
[base_import_async](base_import_async/) | 14.0.1.0.1 | | Import CSV files in the background
[queue_job](queue_job/) | 14.0.3.0.6 | [](https://github.com/guewen) | Job Queue
[queue_job_batch](queue_job_batch/) | 14.0.1.0.0 | | Job Queue Batch
+[queue_job_context](queue_job_context/) | 14.0.1.0.0 | [](https://github.com/AshishHirapara) | Queue Job, prepare context before enqueue keys
[queue_job_cron](queue_job_cron/) | 14.0.1.1.0 | | Scheduled Actions as Queue Jobs
[queue_job_subscribe](queue_job_subscribe/) | 14.0.1.0.0 | | Control which users are subscribed to queue job notifications
[test_base_import_async](test_base_import_async/) | 14.0.1.0.1 | | Test suite for base_import_async. Normally you don't need to install this.
From a1658722d2f8261cf61b800fb01f7487e225b69a Mon Sep 17 00:00:00 2001
From: OCA-git-bot
Date: Wed, 19 Oct 2022 15:04:17 +0000
Subject: [PATCH 244/451] [UPD] README.rst
---
queue_job_context/README.rst | 82 +++++++++++++++++++
.../static/description/index.html | 63 +++++++-------
2 files changed, 111 insertions(+), 34 deletions(-)
diff --git a/queue_job_context/README.rst b/queue_job_context/README.rst
index e69de29bb2..81b2a29d2c 100644
--- a/queue_job_context/README.rst
+++ b/queue_job_context/README.rst
@@ -0,0 +1,82 @@
+=================
+Queue Job Context
+=================
+
+.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! 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-LGPL--3-blue.png
+ :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
+ :alt: License: LGPL-3
+.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fqueue-lightgray.png?logo=github
+ :target: https://github.com/OCA/queue/tree/14.0/queue_job_context
+ :alt: OCA/queue
+.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
+ :target: https://translation.odoo-community.org/projects/queue-14-0/queue-14-0-queue_job_context
+ :alt: Translate me on Weblate
+.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
+ :target: https://runbot.odoo-community.org/runbot/230/14.0
+ :alt: Try me on Runbot
+
+|badge1| |badge2| |badge3| |badge4| |badge5|
+
+Set the list of context keys that are propagated to jobs ("tz", "lang", "allowed_company_ids", "force_company", "active_test"). This will become the default in 16.0.
+
+**Table of contents**
+
+.. contents::
+ :local:
+
+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
+~~~~~~~
+
+* Giovanni Serra - GSLab.it
+* Ooops
+
+Contributors
+~~~~~~~~~~~~
+
+* Ooops - Ashish Hirpara
+
+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.
+
+.. |maintainer-AshishHirapara| image:: https://github.com/AshishHirapara.png?size=40px
+ :target: https://github.com/AshishHirapara
+ :alt: AshishHirapara
+
+Current `maintainer `__:
+
+|maintainer-AshishHirapara|
+
+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_context/static/description/index.html b/queue_job_context/static/description/index.html
index 2a7fb1a505..bdfa34c4f0 100644
--- a/queue_job_context/static/description/index.html
+++ b/queue_job_context/static/description/index.html
@@ -3,8 +3,8 @@
-
-Ooops Queue Job Context
+
+Queue Job Context
-
-
Ooops Queue Job Context
+
+
Queue Job Context
-
-
Queue Job, prepare context before enqueue keys
+
+
Set the list of context keys that are propagated to jobs (“tz”, “lang”, “allowed_company_ids”, “force_company”, “active_test”). This will become the default in 16.0.
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.
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.
From f603580552a444861e6dfc4a473c8a0b613bf7a6 Mon Sep 17 00:00:00 2001
From: OCA-git-bot
Date: Wed, 19 Oct 2022 15:04:17 +0000
Subject: [PATCH 245/451] [ADD] setup.py
---
setup/_metapackage/VERSION.txt | 2 +-
setup/_metapackage/setup.py | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/setup/_metapackage/VERSION.txt b/setup/_metapackage/VERSION.txt
index 2f8ba29a81..786588ab49 100644
--- a/setup/_metapackage/VERSION.txt
+++ b/setup/_metapackage/VERSION.txt
@@ -1 +1 @@
-14.0.20220503.0
\ No newline at end of file
+14.0.20221019.0
\ No newline at end of file
diff --git a/setup/_metapackage/setup.py b/setup/_metapackage/setup.py
index c167121562..c493dfc17a 100644
--- a/setup/_metapackage/setup.py
+++ b/setup/_metapackage/setup.py
@@ -11,6 +11,7 @@
'odoo14-addon-base_import_async',
'odoo14-addon-queue_job',
'odoo14-addon-queue_job_batch',
+ 'odoo14-addon-queue_job_context',
'odoo14-addon-queue_job_cron',
'odoo14-addon-queue_job_subscribe',
'odoo14-addon-test_base_import_async',
From 875b08b175db5688ec4ff4581d06db7796c5ddb9 Mon Sep 17 00:00:00 2001
From: OCA-git-bot
Date: Wed, 19 Oct 2022 15:04:17 +0000
Subject: [PATCH 246/451] queue_job_context 14.0.1.0.1
---
queue_job_context/__manifest__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/queue_job_context/__manifest__.py b/queue_job_context/__manifest__.py
index e5e4cb9d4b..792e0a90f5 100644
--- a/queue_job_context/__manifest__.py
+++ b/queue_job_context/__manifest__.py
@@ -9,7 +9,7 @@
"license": "LGPL-3",
"website": "https://github.com/OCA/queue",
"category": "Tools",
- "version": "14.0.1.0.0",
+ "version": "14.0.1.0.1",
"depends": ["queue_job"],
"installable": True,
}
From 5c1e7231b126f6b99c885b04792b4b39444da3c5 Mon Sep 17 00:00:00 2001
From: OCA-git-bot
Date: Wed, 19 Oct 2022 15:04:27 +0000
Subject: [PATCH 247/451] [UPD] addons table in README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 4068ff4f86..9614ddff71 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,7 @@ addon | version | maintainers | summary
[base_import_async](base_import_async/) | 14.0.1.0.1 | | Import CSV files in the background
[queue_job](queue_job/) | 14.0.3.0.6 | [](https://github.com/guewen) | Job Queue
[queue_job_batch](queue_job_batch/) | 14.0.1.0.0 | | Job Queue Batch
-[queue_job_context](queue_job_context/) | 14.0.1.0.0 | [](https://github.com/AshishHirapara) | Queue Job, prepare context before enqueue keys
+[queue_job_context](queue_job_context/) | 14.0.1.0.1 | [](https://github.com/AshishHirapara) | Queue Job, prepare context before enqueue keys
[queue_job_cron](queue_job_cron/) | 14.0.1.1.0 | | Scheduled Actions as Queue Jobs
[queue_job_subscribe](queue_job_subscribe/) | 14.0.1.0.0 | | Control which users are subscribed to queue job notifications
[test_base_import_async](test_base_import_async/) | 14.0.1.0.1 | | Test suite for base_import_async. Normally you don't need to install this.
From c519d5ca953e4e8475196b4b778f879dcc79424b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Iv=C3=A0n=20Todorovich?=
Date: Wed, 2 Mar 2022 16:08:03 -0300
Subject: [PATCH 248/451] [ADD] queue_job_cron_jobrunner
---
queue_job_cron_jobrunner/README.rst | 1 +
queue_job_cron_jobrunner/__init__.py | 1 +
queue_job_cron_jobrunner/__manifest__.py | 16 ++
queue_job_cron_jobrunner/data/ir_cron.xml | 16 ++
queue_job_cron_jobrunner/models/__init__.py | 2 +
queue_job_cron_jobrunner/models/ir_cron.py | 13 ++
queue_job_cron_jobrunner/models/queue_job.py | 164 ++++++++++++++++++
queue_job_cron_jobrunner/readme/CONFIGURE.rst | 21 +++
.../readme/CONTRIBUTORS.rst | 3 +
.../readme/DESCRIPTION.rst | 14 ++
queue_job_cron_jobrunner/readme/ROADMAP.rst | 3 +
queue_job_cron_jobrunner/tests/__init__.py | 1 +
.../tests/test_queue_job.py | 54 ++++++
queue_job_cron_jobrunner/views/ir_cron.xml | 22 +++
.../odoo/addons/queue_job_cron_jobrunner | 1 +
setup/queue_job_cron_jobrunner/setup.py | 6 +
16 files changed, 338 insertions(+)
create mode 100644 queue_job_cron_jobrunner/README.rst
create mode 100644 queue_job_cron_jobrunner/__init__.py
create mode 100644 queue_job_cron_jobrunner/__manifest__.py
create mode 100644 queue_job_cron_jobrunner/data/ir_cron.xml
create mode 100644 queue_job_cron_jobrunner/models/__init__.py
create mode 100644 queue_job_cron_jobrunner/models/ir_cron.py
create mode 100644 queue_job_cron_jobrunner/models/queue_job.py
create mode 100644 queue_job_cron_jobrunner/readme/CONFIGURE.rst
create mode 100644 queue_job_cron_jobrunner/readme/CONTRIBUTORS.rst
create mode 100644 queue_job_cron_jobrunner/readme/DESCRIPTION.rst
create mode 100644 queue_job_cron_jobrunner/readme/ROADMAP.rst
create mode 100644 queue_job_cron_jobrunner/tests/__init__.py
create mode 100644 queue_job_cron_jobrunner/tests/test_queue_job.py
create mode 100644 queue_job_cron_jobrunner/views/ir_cron.xml
create mode 120000 setup/queue_job_cron_jobrunner/odoo/addons/queue_job_cron_jobrunner
create mode 100644 setup/queue_job_cron_jobrunner/setup.py
diff --git a/queue_job_cron_jobrunner/README.rst b/queue_job_cron_jobrunner/README.rst
new file mode 100644
index 0000000000..d0cc046d68
--- /dev/null
+++ b/queue_job_cron_jobrunner/README.rst
@@ -0,0 +1 @@
+TO BE GENERATED AUTOMATICALLY
diff --git a/queue_job_cron_jobrunner/__init__.py b/queue_job_cron_jobrunner/__init__.py
new file mode 100644
index 0000000000..0650744f6b
--- /dev/null
+++ b/queue_job_cron_jobrunner/__init__.py
@@ -0,0 +1 @@
+from . import models
diff --git a/queue_job_cron_jobrunner/__manifest__.py b/queue_job_cron_jobrunner/__manifest__.py
new file mode 100644
index 0000000000..6c0caa52f9
--- /dev/null
+++ b/queue_job_cron_jobrunner/__manifest__.py
@@ -0,0 +1,16 @@
+{
+ "name": "Queue Job Cron Jobrunner",
+ "summary": "Run jobs without a dedicated JobRunner",
+ "version": "15.0.1.0.0",
+ "development_status": "Alpha",
+ "author": "Camptocamp SA, Odoo Community Association (OCA)",
+ "maintainers": ["ivantodorovich"],
+ "website": "https://github.com/OCA/queue",
+ "license": "AGPL-3",
+ "category": "Others",
+ "depends": ["queue_job"],
+ "data": [
+ "data/ir_cron.xml",
+ "views/ir_cron.xml",
+ ],
+}
diff --git a/queue_job_cron_jobrunner/data/ir_cron.xml b/queue_job_cron_jobrunner/data/ir_cron.xml
new file mode 100644
index 0000000000..5cb7436dcc
--- /dev/null
+++ b/queue_job_cron_jobrunner/data/ir_cron.xml
@@ -0,0 +1,16 @@
+
+
+
+
+ Queue Job Runner
+
+ code
+ model._job_runner()
+
+
+ 1
+ days
+ -1
+
+
+
diff --git a/queue_job_cron_jobrunner/models/__init__.py b/queue_job_cron_jobrunner/models/__init__.py
new file mode 100644
index 0000000000..4ba9dd1a16
--- /dev/null
+++ b/queue_job_cron_jobrunner/models/__init__.py
@@ -0,0 +1,2 @@
+from . import ir_cron
+from . import queue_job
diff --git a/queue_job_cron_jobrunner/models/ir_cron.py b/queue_job_cron_jobrunner/models/ir_cron.py
new file mode 100644
index 0000000000..61235dc45a
--- /dev/null
+++ b/queue_job_cron_jobrunner/models/ir_cron.py
@@ -0,0 +1,13 @@
+# Copyright 2022 Camptocamp SA (https://www.camptocamp.com).
+# @author Iván Todorovich
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+
+from odoo import fields, models
+
+
+class IrCron(models.Model):
+ _inherit = "ir.cron"
+
+ queue_job_runner = fields.Boolean(
+ help="If checked, the cron is considered to be a queue.job runner.",
+ )
diff --git a/queue_job_cron_jobrunner/models/queue_job.py b/queue_job_cron_jobrunner/models/queue_job.py
new file mode 100644
index 0000000000..6a374fcb22
--- /dev/null
+++ b/queue_job_cron_jobrunner/models/queue_job.py
@@ -0,0 +1,164 @@
+# Copyright 2022 Camptocamp SA (https://www.camptocamp.com).
+# @author Iván Todorovich
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+
+import logging
+import traceback
+from io import StringIO
+
+from psycopg2 import OperationalError
+
+from odoo import _, api, models, tools
+from odoo.service.model import PG_CONCURRENCY_ERRORS_TO_RETRY
+
+from odoo.addons.queue_job.controllers.main import PG_RETRY
+from odoo.addons.queue_job.exception import (
+ FailedJobError,
+ NothingToDoJob,
+ RetryableJobError,
+)
+from odoo.addons.queue_job.job import Job
+
+_logger = logging.getLogger(__name__)
+
+
+class QueueJob(models.Model):
+ _inherit = "queue.job"
+
+ @api.model
+ def _acquire_one_job(self):
+ """Acquire the next job to be run.
+
+ :returns: queue.job record (locked for update)
+ """
+ # TODO: This method should respect channel priority and capacity,
+ # rather than just fetching them by creation date.
+ self.flush()
+ self.env.cr.execute(
+ """
+ SELECT id
+ FROM queue_job
+ WHERE state = 'pending'
+ AND (eta IS NULL OR eta <= (now() AT TIME ZONE 'UTC'))
+ ORDER BY date_created DESC
+ LIMIT 1 FOR NO KEY UPDATE SKIP LOCKED
+ """
+ )
+ row = self.env.cr.fetchone()
+ return self.browse(row and row[0])
+
+ def _process(self, commit=False):
+ """Process the job"""
+ self.ensure_one()
+ job = Job._load_from_db_record(self)
+ # Set it as started
+ job.set_started()
+ job.store()
+ _logger.debug("%s started", job.uuid)
+ # TODO: Commit the state change so that the state can be read from the UI
+ # while the job is processing. However, doing this will release the
+ # lock on the db, so we need to find another way.
+ # if commit:
+ # self.flush()
+ # self.env.cr.commit()
+
+ # Actual processing
+ try:
+ try:
+ with self.env.cr.savepoint():
+ job.perform()
+ job.set_done()
+ job.store()
+ except OperationalError as err:
+ # Automatically retry the typical transaction serialization errors
+ if err.pgcode not in PG_CONCURRENCY_ERRORS_TO_RETRY:
+ raise
+ message = tools.ustr(err.pgerror, errors="replace")
+ job.postpone(result=message, seconds=PG_RETRY)
+ job.set_pending(reset_retry=False)
+ job.store()
+ _logger.debug("%s OperationalError, postponed", job)
+
+ except NothingToDoJob as err:
+ if str(err):
+ msg = str(err)
+ else:
+ msg = _("Job interrupted and set to Done: nothing to do.")
+ job.set_done(msg)
+ job.store()
+
+ except RetryableJobError as err:
+ # delay the job later, requeue
+ job.postpone(result=str(err), seconds=5)
+ job.set_pending(reset_retry=False)
+ job.store()
+ _logger.debug("%s postponed", job)
+
+ except (FailedJobError, Exception):
+ with StringIO() as buff:
+ traceback.print_exc(file=buff)
+ _logger.error(buff.getvalue())
+ job.set_failed(exc_info=buff.getvalue())
+ job.store()
+
+ if commit: # pragma: no cover
+ self.env["base"].flush()
+ self.env.cr.commit() # pylint: disable=invalid-commit
+
+ @api.model
+ def _job_runner(self, commit=True):
+ """Short-lived job runner, triggered by async crons"""
+ job = self._acquire_one_job()
+ while job:
+ job._process(commit=commit)
+ job = self._acquire_one_job()
+ # TODO: If limit_time_real_cron is reached before all the jobs are done,
+ # the worker will be killed abruptly.
+ # Ideally, find a way to know if we're close to reaching this limit,
+ # stop processing, and trigger a new execution to continue.
+ #
+ # if job and limit_time_real_cron_reached_or_about_to_reach:
+ # self._cron_trigger()
+ # break
+
+ @api.model
+ def _cron_trigger(self, at=None):
+ """Trigger the cron job runners
+
+ Odoo will prevent concurrent cron jobs from running.
+ So, to support parallel execution, we'd need to have (at least) the
+ same number of ir.crons records as cron workers.
+
+ All crons should be triggered at the same time.
+ """
+ crons = self.env["ir.cron"].sudo().search([("queue_job_runner", "=", True)])
+ for cron in crons:
+ cron._trigger(at=at)
+
+ def _ensure_cron_trigger(self):
+ """Create cron triggers for these jobs"""
+ records = self.filtered(lambda r: r.state == "pending")
+ if not records:
+ return
+ # Trigger immediate runs
+ immediate = any(not rec.eta for rec in records)
+ if immediate:
+ self._cron_trigger()
+ # Trigger delayed eta runs
+ delayed_etas = {rec.eta for rec in records if rec.eta}
+ if delayed_etas:
+ self._cron_trigger(at=list(delayed_etas))
+
+ @api.model_create_multi
+ def create(self, vals_list):
+ # When jobs are created, also create the cron trigger
+ records = super().create(vals_list)
+ records._ensure_cron_trigger()
+ return records
+
+ def write(self, vals):
+ # When a job state or eta changes, make sure a cron trigger is created
+ res = super().write(vals)
+ if "state" in vals or "eta" in vals:
+ self._ensure_cron_trigger()
+ return res
diff --git a/queue_job_cron_jobrunner/readme/CONFIGURE.rst b/queue_job_cron_jobrunner/readme/CONFIGURE.rst
new file mode 100644
index 0000000000..0de15596d2
--- /dev/null
+++ b/queue_job_cron_jobrunner/readme/CONFIGURE.rst
@@ -0,0 +1,21 @@
+.. warning::
+
+ Don't use this module if you're already running the regular ``queue_job`` runner.
+
+
+For the easiest case, no configuration is required besides installing the module.
+
+To avoid CronWorker CPU timeout from abruptly stopping the job processing cron, it's
+recommended to launch Odoo with ``--limit-time-real-cron=0``, to disable the CronWorker
+timeout altogether.
+
+.. note::
+
+ In Odoo.sh, this is done by default.
+
+
+Parallel execution of jobs can be achieved by leveraging multiple ``ir.cron`` records:
+
+* Make sure you have enough CronWorkers available (Odoo CLI ``--max-cron-threads``)
+* Duplicate the ``queue_job_cron`` cron record as many times as needed, until you have
+ as much records as cron workers.
diff --git a/queue_job_cron_jobrunner/readme/CONTRIBUTORS.rst b/queue_job_cron_jobrunner/readme/CONTRIBUTORS.rst
new file mode 100644
index 0000000000..59b447f28a
--- /dev/null
+++ b/queue_job_cron_jobrunner/readme/CONTRIBUTORS.rst
@@ -0,0 +1,3 @@
+* `Camptocamp `_
+
+ * Iván Todorovich
diff --git a/queue_job_cron_jobrunner/readme/DESCRIPTION.rst b/queue_job_cron_jobrunner/readme/DESCRIPTION.rst
new file mode 100644
index 0000000000..e713aa89a9
--- /dev/null
+++ b/queue_job_cron_jobrunner/readme/DESCRIPTION.rst
@@ -0,0 +1,14 @@
+This module implements a simple ``queue.job`` runner using ``ir.cron`` triggers.
+
+It's meant to be used on environments where the regular job runner can't be run, like
+on Odoo.sh.
+
+Unlike the regular job runner, where jobs are dispatched to the HttpWorkers, jobs are
+processed on the CronWorker threads by the job runner crons. This is a design decision
+because:
+
+* Odoo.sh puts HttpWorkers to sleep when there's no network activity
+* HttpWorkers are meant for traffic. Users shouldn't pay the price of background tasks.
+
+For now, it only implements the most basic features of the ``queue_job`` runner, notably
+no channel capacity nor priorities. Please check the ROADMAP for further details.
diff --git a/queue_job_cron_jobrunner/readme/ROADMAP.rst b/queue_job_cron_jobrunner/readme/ROADMAP.rst
new file mode 100644
index 0000000000..b82e199202
--- /dev/null
+++ b/queue_job_cron_jobrunner/readme/ROADMAP.rst
@@ -0,0 +1,3 @@
+* Support channel capacity and priority. (See ``_acquire_one_job``)
+* Gracefully handle CronWorker CPU timeouts. (See ``_job_runner``)
+* Commit transaction after job state updated to started. (See ``_process``)
diff --git a/queue_job_cron_jobrunner/tests/__init__.py b/queue_job_cron_jobrunner/tests/__init__.py
new file mode 100644
index 0000000000..42bd479ed0
--- /dev/null
+++ b/queue_job_cron_jobrunner/tests/__init__.py
@@ -0,0 +1 @@
+from . import test_queue_job
diff --git a/queue_job_cron_jobrunner/tests/test_queue_job.py b/queue_job_cron_jobrunner/tests/test_queue_job.py
new file mode 100644
index 0000000000..3798e63778
--- /dev/null
+++ b/queue_job_cron_jobrunner/tests/test_queue_job.py
@@ -0,0 +1,54 @@
+# Copyright 2022 Camptocamp SA (https://www.camptocamp.com).
+# @author Iván Todorovich
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+
+from datetime import timedelta
+
+from freezegun import freeze_time
+
+from odoo import fields
+from odoo.tests.common import TransactionCase
+from odoo.tools import mute_logger
+
+
+class TestQueueJob(TransactionCase):
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+ cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
+ cls.cron = cls.env.ref("queue_job_cron_jobrunner.queue_job_cron")
+ # Cleanup triggers just in case
+ cls.env["ir.cron.trigger"].search([]).unlink()
+
+ def assertTriggerAt(self, at, message=None):
+ """Ensures a cron trigger is created at the given time"""
+ return self.assertTrue(
+ self.env["ir.cron.trigger"].search([("call_at", "=", at)]),
+ message,
+ )
+
+ @freeze_time("2022-02-22 22:22:22")
+ def test_queue_job_cron_trigger(self):
+ """Test that ir.cron triggers are created for every queue.job"""
+ job = self.env["res.partner"].with_delay().create({"name": "test"})
+ job_record = job.db_record()
+ self.assertTriggerAt(fields.Datetime.now(), "Trigger should've been created")
+ job_record.eta = fields.Datetime.now() + timedelta(hours=1)
+ self.assertTriggerAt(job_record.eta, "A new trigger should've been created")
+
+ @mute_logger("odoo.addons.queue_job_cron_jobrunner.models.queue_job")
+ def test_queue_job_process(self):
+ """Test that jobs are processed by the queue job cron"""
+ # Create some jobs
+ job1 = self.env["res.partner"].with_delay().create({"name": "test"})
+ job1_record = job1.db_record()
+ job2 = self.env["res.partner"].with_delay().create(False)
+ job2_record = job2.db_record()
+ job3 = self.env["res.partner"].with_delay(eta=3600).create({"name": "Test"})
+ job3_record = job3.db_record()
+ # Run the job processing cron
+ self.env["queue.job"]._job_runner(commit=False)
+ # Check that the jobs were processed
+ self.assertEqual(job1_record.state, "done", "Processed OK")
+ self.assertEqual(job2_record.state, "failed", "Has errors")
+ self.assertEqual(job3_record.state, "pending", "Still pending, because of eta")
diff --git a/queue_job_cron_jobrunner/views/ir_cron.xml b/queue_job_cron_jobrunner/views/ir_cron.xml
new file mode 100644
index 0000000000..f1fe6a7455
--- /dev/null
+++ b/queue_job_cron_jobrunner/views/ir_cron.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+ ir.cron
+
+
+
+
+
+
+
+
+
diff --git a/setup/queue_job_cron_jobrunner/odoo/addons/queue_job_cron_jobrunner b/setup/queue_job_cron_jobrunner/odoo/addons/queue_job_cron_jobrunner
new file mode 120000
index 0000000000..9ec9686861
--- /dev/null
+++ b/setup/queue_job_cron_jobrunner/odoo/addons/queue_job_cron_jobrunner
@@ -0,0 +1 @@
+../../../../queue_job_cron_jobrunner
\ No newline at end of file
diff --git a/setup/queue_job_cron_jobrunner/setup.py b/setup/queue_job_cron_jobrunner/setup.py
new file mode 100644
index 0000000000..28c57bb640
--- /dev/null
+++ b/setup/queue_job_cron_jobrunner/setup.py
@@ -0,0 +1,6 @@
+import setuptools
+
+setuptools.setup(
+ setup_requires=['setuptools-odoo'],
+ odoo_addon=True,
+)
From 46a7c8e38c667a1d121f3e01f037d678e9ac8db9 Mon Sep 17 00:00:00 2001
From: Guewen Baconnier
Date: Tue, 1 Nov 2022 14:52:01 +0100
Subject: [PATCH 249/451] Add enqueue of graph dependencies in
queue_job_cron_jobrunner
---
queue_job_cron_jobrunner/models/queue_job.py | 4 ++++
.../tests/test_queue_job.py | 17 +++++++++++++++++
2 files changed, 21 insertions(+)
diff --git a/queue_job_cron_jobrunner/models/queue_job.py b/queue_job_cron_jobrunner/models/queue_job.py
index 6a374fcb22..4efd8b21d5 100644
--- a/queue_job_cron_jobrunner/models/queue_job.py
+++ b/queue_job_cron_jobrunner/models/queue_job.py
@@ -105,6 +105,10 @@ def _process(self, commit=False):
self.env["base"].flush()
self.env.cr.commit() # pylint: disable=invalid-commit
+ _logger.debug("%s enqueue depends started", job)
+ job.enqueue_waiting()
+ _logger.debug("%s enqueue depends done", job)
+
@api.model
def _job_runner(self, commit=True):
"""Short-lived job runner, triggered by async crons"""
diff --git a/queue_job_cron_jobrunner/tests/test_queue_job.py b/queue_job_cron_jobrunner/tests/test_queue_job.py
index 3798e63778..3f2e0ef637 100644
--- a/queue_job_cron_jobrunner/tests/test_queue_job.py
+++ b/queue_job_cron_jobrunner/tests/test_queue_job.py
@@ -52,3 +52,20 @@ def test_queue_job_process(self):
self.assertEqual(job1_record.state, "done", "Processed OK")
self.assertEqual(job2_record.state, "failed", "Has errors")
self.assertEqual(job3_record.state, "pending", "Still pending, because of eta")
+
+ @freeze_time("2022-02-22 22:22:22")
+ def test_queue_job_cron_trigger_enqueue_dependencies(self):
+ """Test that ir.cron execution enqueue waiting dependencies"""
+ delayable = self.env["res.partner"].delayable().create({"name": "test"})
+ delayable2 = self.env["res.partner"].delayable().create({"name": "test2"})
+ delayable.on_done(delayable2)
+ delayable.delay()
+ job_record = delayable._generated_job.db_record()
+ job_record_depends = delayable2._generated_job.db_record()
+
+ self.env["queue.job"]._job_runner(commit=False)
+
+ self.assertEqual(job_record.state, "done", "Processed OK")
+ # if the state is "waiting_dependencies", it means the "enqueue_waiting()"
+ # step has not been doen when the parent job has been done
+ self.assertEqual(job_record_depends.state, "done", "Processed OK")
From 9513d9dff213abb717be8202ff66bdd7aff720d2 Mon Sep 17 00:00:00 2001
From: Chris Bergman
Date: Mon, 31 Oct 2022 19:38:25 +0100
Subject: [PATCH 250/451] Backport from 15.0 to 14.0 for
queue_job_cron_jobrunner
---
README.md | 1 +
queue_job_cron_jobrunner/README.rst | 138 ++++-
queue_job_cron_jobrunner/__manifest__.py | 2 +-
.../i18n/queue_job_cron_jobrunner.pot | 43 ++
queue_job_cron_jobrunner/models/queue_job.py | 17 +-
.../static/description/icon.png | Bin 0 -> 9455 bytes
.../static/description/index.html | 475 ++++++++++++++++++
.../tests/test_queue_job.py | 30 +-
setup/_metapackage/VERSION.txt | 1 -
setup/_metapackage/setup.py | 26 -
.../odoo/addons/base_import_async | 2 +-
.../odoo/addons/test_base_import_async | 2 +-
12 files changed, 678 insertions(+), 59 deletions(-)
create mode 100644 queue_job_cron_jobrunner/i18n/queue_job_cron_jobrunner.pot
create mode 100644 queue_job_cron_jobrunner/static/description/icon.png
create mode 100644 queue_job_cron_jobrunner/static/description/index.html
delete mode 100644 setup/_metapackage/VERSION.txt
delete mode 100644 setup/_metapackage/setup.py
diff --git a/README.md b/README.md
index 9614ddff71..b3d7e77e27 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,7 @@ addon | version | maintainers | summary
[queue_job_batch](queue_job_batch/) | 14.0.1.0.0 | | Job Queue Batch
[queue_job_context](queue_job_context/) | 14.0.1.0.1 | [](https://github.com/AshishHirapara) | Queue Job, prepare context before enqueue keys
[queue_job_cron](queue_job_cron/) | 14.0.1.1.0 | | Scheduled Actions as Queue Jobs
+[queue_job_cron_jobrunner](queue_job_cron_jobrunner/) | 14.0.1.0.0 | | Run jobs without a dedicated JobRunner
[queue_job_subscribe](queue_job_subscribe/) | 14.0.1.0.0 | | Control which users are subscribed to queue job notifications
[test_base_import_async](test_base_import_async/) | 14.0.1.0.1 | | Test suite for base_import_async. Normally you don't need to install this.
[test_queue_job](test_queue_job/) | 14.0.3.0.1 | | Queue Job Tests
diff --git a/queue_job_cron_jobrunner/README.rst b/queue_job_cron_jobrunner/README.rst
index d0cc046d68..26fd243a6d 100644
--- a/queue_job_cron_jobrunner/README.rst
+++ b/queue_job_cron_jobrunner/README.rst
@@ -1 +1,137 @@
-TO BE GENERATED AUTOMATICALLY
+========================
+Queue Job Cron Jobrunner
+========================
+
+.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! This file is generated by oca-gen-addon-readme !!
+ !! changes will be overwritten. !!
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png
+ :target: https://odoo-community.org/page/development-status
+ :alt: Alpha
+.. |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/14.0/queue_job_cron_jobrunner
+ :alt: OCA/queue
+.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
+ :target: https://translation.odoo-community.org/projects/queue-14-0/queue-14-0-queue_job_cron_jobrunner
+ :alt: Translate me on Weblate
+.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
+ :target: https://runbot.odoo-community.org/runbot/230/14.0
+ :alt: Try me on Runbot
+
+|badge1| |badge2| |badge3| |badge4| |badge5|
+
+This module implements a simple ``queue.job`` runner using ``ir.cron`` triggers.
+
+It's meant to be used on environments where the regular job runner can't be run, like
+on Odoo.sh.
+
+Unlike the regular job runner, where jobs are dispatched to the HttpWorkers, jobs are
+processed on the CronWorker threads by the job runner crons. This is a design decision
+because:
+
+* Odoo.sh puts HttpWorkers to sleep when there's no network activity
+* HttpWorkers are meant for traffic. Users shouldn't pay the price of background tasks.
+
+For now, it only implements the most basic features of the ``queue_job`` runner, notably
+no channel capacity nor priorities. Please check the ROADMAP for further details.
+
+.. IMPORTANT::
+ This is an alpha version, the data model and design can change at any time without warning.
+ Only for development or testing purpose, do not use in production.
+ `More details on development status `_
+
+**Table of contents**
+
+.. contents::
+ :local:
+
+Configuration
+=============
+
+.. warning::
+
+ Don't use this module if you're already running the regular ``queue_job`` runner.
+
+
+For the easiest case, no configuration is required besides installing the module.
+
+To avoid CronWorker CPU timeout from abruptly stopping the job processing cron, it's
+recommended to launch Odoo with ``--limit-time-real-cron=0``, to disable the CronWorker
+timeout altogether.
+
+.. note::
+
+ In Odoo.sh, this is done by default.
+
+
+Parallel execution of jobs can be achieved by leveraging multiple ``ir.cron`` records:
+
+* Make sure you have enough CronWorkers available (Odoo CLI ``--max-cron-threads``)
+* Duplicate the ``queue_job_cron`` cron record as many times as needed, until you have
+ as much records as cron workers.
+
+Known issues / Roadmap
+======================
+
+* Support channel capacity and priority. (See ``_acquire_one_job``)
+* Gracefully handle CronWorker CPU timeouts. (See ``_job_runner``)
+* Commit transaction after job state updated to started. (See ``_process``)
+
+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 SA
+
+Contributors
+~~~~~~~~~~~~
+
+* `Camptocamp `_
+
+ * Iván Todorovich
+
+* `codeforward B.V. `_
+
+ * Chris Bergman
+
+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.
+
+.. |maintainer-ivantodorovich| image:: https://github.com/ivantodorovich.png?size=40px
+ :target: https://github.com/ivantodorovich
+ :alt: ivantodorovich
+
+Current `maintainer `__:
+
+|maintainer-ivantodorovich|
+
+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_cron_jobrunner/__manifest__.py b/queue_job_cron_jobrunner/__manifest__.py
index 6c0caa52f9..4891883594 100644
--- a/queue_job_cron_jobrunner/__manifest__.py
+++ b/queue_job_cron_jobrunner/__manifest__.py
@@ -1,7 +1,7 @@
{
"name": "Queue Job Cron Jobrunner",
"summary": "Run jobs without a dedicated JobRunner",
- "version": "15.0.1.0.0",
+ "version": "14.0.1.0.1",
"development_status": "Alpha",
"author": "Camptocamp SA, Odoo Community Association (OCA)",
"maintainers": ["ivantodorovich"],
diff --git a/queue_job_cron_jobrunner/i18n/queue_job_cron_jobrunner.pot b/queue_job_cron_jobrunner/i18n/queue_job_cron_jobrunner.pot
new file mode 100644
index 0000000000..d81b10a5bd
--- /dev/null
+++ b/queue_job_cron_jobrunner/i18n/queue_job_cron_jobrunner.pot
@@ -0,0 +1,43 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * queue_job_cron_jobrunner
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 15.0\n"
+"Report-Msgid-Bugs-To: \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: queue_job_cron_jobrunner
+#: model:ir.model.fields,help:queue_job_cron_jobrunner.field_ir_cron__queue_job_runner
+msgid "If checked, the cron is considered to be a queue.job runner."
+msgstr ""
+
+#. module: queue_job_cron_jobrunner
+#: code:addons/queue_job_cron_jobrunner/models/queue_job.py:0
+#, python-format
+msgid "Job interrupted and set to Done: nothing to do."
+msgstr ""
+
+#. module: queue_job_cron_jobrunner
+#: model:ir.model,name:queue_job_cron_jobrunner.model_queue_job
+msgid "Queue Job"
+msgstr ""
+
+#. module: queue_job_cron_jobrunner
+#: model:ir.actions.server,name:queue_job_cron_jobrunner.queue_job_cron_ir_actions_server
+#: model:ir.cron,cron_name:queue_job_cron_jobrunner.queue_job_cron
+#: model:ir.cron,name:queue_job_cron_jobrunner.queue_job_cron
+#: model:ir.model.fields,field_description:queue_job_cron_jobrunner.field_ir_cron__queue_job_runner
+msgid "Queue Job Runner"
+msgstr ""
+
+#. module: queue_job_cron_jobrunner
+#: model:ir.model,name:queue_job_cron_jobrunner.model_ir_cron
+msgid "Scheduled Actions"
+msgstr ""
diff --git a/queue_job_cron_jobrunner/models/queue_job.py b/queue_job_cron_jobrunner/models/queue_job.py
index 4efd8b21d5..ba5825410e 100644
--- a/queue_job_cron_jobrunner/models/queue_job.py
+++ b/queue_job_cron_jobrunner/models/queue_job.py
@@ -4,11 +4,12 @@
import logging
import traceback
+from datetime import datetime
from io import StringIO
from psycopg2 import OperationalError
-from odoo import _, api, models, tools
+from odoo import _, api, fields, models, tools
from odoo.service.model import PG_CONCURRENCY_ERRORS_TO_RETRY
from odoo.addons.queue_job.controllers.main import PG_RETRY
@@ -135,9 +136,21 @@ def _cron_trigger(self, at=None):
All crons should be triggered at the same time.
"""
+ if at is not None:
+ if isinstance(at, list) and not all([isinstance(x, datetime) for x in at]):
+ raise TypeError(f"Invalid parameter 'at': {str(at)}")
+ elif not isinstance(at, list) and not isinstance(at, datetime):
+ raise TypeError(f"Invalid parameter 'at': {str(at)}")
crons = self.env["ir.cron"].sudo().search([("queue_job_runner", "=", True)])
+ nextcall = fields.Datetime.now()
+ if at is not None:
+ if isinstance(at, list) and len(at):
+ nextcall = sorted(at).pop()
+ elif isinstance(at, datetime):
+ nextcall = at
for cron in crons:
- cron._trigger(at=at)
+ if nextcall < cron.nextcall:
+ cron.nextcall = nextcall
def _ensure_cron_trigger(self):
"""Create cron triggers for these jobs"""
diff --git a/queue_job_cron_jobrunner/static/description/icon.png b/queue_job_cron_jobrunner/static/description/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d
GIT binary patch
literal 9455
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
z
z?n?eq1XP>p-IM$Z^C;2L3itnbJZAip*Zo0aw2bs8@(s^~*8T9go!%dHcAz2lM;`yp
zD=7&xjFV$S&5uDaiScyD?B-i1ze`+CoRtz`Wn+Zls4&}MO{@N!ufrzjG$B79)Y2d3tBk&)TxUTw@QS0TEL_?njX|@vq?Uz(nBFK5Pq7*xj#u*R&i|?7+6#
z+|r_n#SW&LXhtheZdah{ZVoqwyT{D>MC3nkFF#N)xLi{p7J1jXlmVeb;cP5?e(=f#
zuT7fvjSbjS781v?7{)-X3*?>tq?)Yd)~|1{BDS(pqC
zC}~H#WXlkUW*H5CDOo<)#x7%RY)A;ShGhI5s*#cRDA8YgqG(HeKDx+#(ZQ?386dv!
zlXCO)w91~Vw4AmOcATuV653fa9R$fyK8ul%rG
z-wfS
zihugoZyr38Im?Zuh6@RcF~t1anQu7>#lPpb#}4cOA!EM11`%f*07RqOVkmX{p~KJ9
z^zP;K#|)$`^Rb{rnHGH{~>1(fawV0*Z#)}M`m8-?ZJV<+e}s9wE#
z)l&az?w^5{)`S(%MRzxdNqrs1n*-=jS^_jqE*5XDrA0+VE`5^*p3CuM<&dZEeCjoz
zR;uu_H9ZPZV|fQq`Cyw4nscrVwi!fE6ciMmX$!_hN7uF;jjKG)d2@aC4ropY)8etW=xJvni)8eHi`H$%#zn^WJ5NLc-rqk|u&&4Z6fD_m&JfSI1Bvb?b<*n&sfl0^t
z=HnmRl`XrFvMKB%9}>PaA`m-fK6a0(8=qPkWS5bb4=v?XcWi&hRY?O5HdulRi4?fN
zlsJ*N-0Qw+Yic@s0(2uy%F@ib;GjXt01Fmx5XbRo6+n|pP(&nodMoap^z{~q
ziEeaUT@Mxe3vJSfI6?uLND(CNr=#^W<1b}jzW58bIfyWTDle$mmS(|x-0|2UlX+9k
zQ^EX7Nw}?EzVoBfT(-LT|=9N@^hcn-_p&sqG
z&*oVs2JSU+N4ZD`FhCAWaS;>|wH2G*Id|?pa#@>tyxX`+4HyIArWDvVrX)2WAOQff
z0qyHu&-S@i^MS-+j--!pr4fPBj~_8({~e1bfcl0wI1kaoN>mJL6KUPQm5N7lB(ui1
zE-o%kq)&djzWJ}ob<-GfDlkB;F31j-VHKvQUGQ3sp`CwyGJk_i!y^sD0fqC@$9|jO
zOqN!r!8-p==F@ZVP=U$qSpY(gQ0)59P1&t@y?5rvg<}E+GB}26NYPp4f2YFQrQtot5mn3wu_qprZ=>Ig-$
zbW26Ws~IgY>}^5w`vTB(G`PTZaDiGBo5o(tp)qli|NeV(
z@H_=R8V39rt5J5YB2Ky?4eJJ#b`_iBe2ot~6%7mLt5t8Vwi^Jy7|jWXqa3amOIoRb
zOr}WVFP--DsS`1WpN%~)t3R!arKF^Q$e12KEqU36AWwnCBICpH4XCsfnyrHr>$I$4
z!DpKX$OKLWarN7nv@!uIA+~RNO)l$$w}p(;b>mx8pwYvu;dD_unryX_NhT8*Tj>BTrTTL&!?O+%Rv;b?B??gSzdp?6Uug9{
zd@V08Z$BdI?fpoCS$)t4mg4rT8Q_I}h`0d-vYZ^|dOB*Q^S|xqTV*vIg?@fVFSmMpaw0qtTRbx}
z({Pg?#{2`sc9)M5N$*N|4;^t$+QP?#mov
zGVC@I*lBVrOU-%2y!7%)fAKjpEFsgQc4{amtiHb95KQEwvf<(3T<9-Zm$xIew#P22
zc2Ix|App^>v6(3L_MCU0d3W##AB0M~3D00EWoKZqsJYT(#@w$Y_H7G22M~ApVFTRHMI_3be)Lkn#0F*V8Pq
zc}`Cjy$bE;FJ6H7p=0y#R>`}-m4(0F>%@P|?7fx{=R^uFdISRnZ2W_xQhD{YuR3t<
z{6yxu=4~JkeA;|(J6_nv#>Nvs&FuLA&PW^he@t(UwFFE8)|a!R{`E`K`i^ZnyE4$k
z;(749Ix|oi$c3QbEJ3b~D_kQsPz~fIUKym($a_7dJ?o+40*OLl^{=&oq$<#Q(yyrp
z{J-FAniyAw9tPbe&IhQ|a`DqFTVQGQ&Gq3!C2==4x{6EJwiPZ8zub-iXoUtkJiG{}
zPaR&}_fn8_z~(=;5lD-aPWD3z8PZS@AaUiomF!G8I}Mf>e~0g#BelA-5#`cj;O5>N
Xviia!U7SGha1wx#SCgwmn*{w2TRX*I
literal 0
HcmV?d00001
diff --git a/queue_job_cron_jobrunner/static/description/index.html b/queue_job_cron_jobrunner/static/description/index.html
new file mode 100644
index 0000000000..a878a31c23
--- /dev/null
+++ b/queue_job_cron_jobrunner/static/description/index.html
@@ -0,0 +1,475 @@
+
+
+
+
+
+
+Queue Job Cron Jobrunner
+
+
+
+
+
Queue Job Cron Jobrunner
+
+
+
+
This module implements a simple queue.job runner using ir.cron triggers.
+
It’s meant to be used on environments where the regular job runner can’t be run, like
+on Odoo.sh.
+
Unlike the regular job runner, where jobs are dispatched to the HttpWorkers, jobs are
+processed on the CronWorker threads by the job runner crons. This is a design decision
+because:
+
+
Odoo.sh puts HttpWorkers to sleep when there’s no network activity
+
HttpWorkers are meant for traffic. Users shouldn’t pay the price of background tasks.
+
+
For now, it only implements the most basic features of the queue_job runner, notably
+no channel capacity nor priorities. Please check the ROADMAP for further details.
+
+
Important
+
This is an alpha version, the data model and design can change at any time without warning.
+Only for development or testing purpose, do not use in production.
+More details on development status
Don’t use this module if you’re already running the regular queue_job runner.
+
+
For the easiest case, no configuration is required besides installing the module.
+
To avoid CronWorker CPU timeout from abruptly stopping the job processing cron, it’s
+recommended to launch Odoo with --limit-time-real-cron=0, to disable the CronWorker
+timeout altogether.
+
+
Note
+
In Odoo.sh, this is done by default.
+
+
Parallel execution of jobs can be achieved by leveraging multiple ir.cron records:
+
+
Make sure you have enough CronWorkers available (Odoo CLI --max-cron-threads)
+
Duplicate the queue_job_cron cron record as many times as needed, until you have
+as much records as cron workers.
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.
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.
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.
diff --git a/base_export_async/security/ir_rule.xml b/base_export_async/security/ir_rule.xml
index 575df5b6b6..d5bbd85e41 100644
--- a/base_export_async/security/ir_rule.xml
+++ b/base_export_async/security/ir_rule.xml
@@ -1,13 +1,13 @@
-
+
Only user can read delay.export
-
-
-
-
-
-
+
+
+
+
+
+ [('user_ids', 'in', user.id)]
diff --git a/base_export_async/static/src/js/data_export.js b/base_export_async/static/src/js/data_export.js
index 9e5065cd2a..427cb9c038 100644
--- a/base_export_async/static/src/js/data_export.js
+++ b/base_export_async/static/src/js/data_export.js
@@ -1,11 +1,11 @@
-odoo.define('base_export_async.DataExport', function(require) {
+odoo.define("base_export_async.DataExport", function (require) {
"use strict";
- var core = require('web.core');
- var DataExport = require('web.DataExport');
- var framework = require('web.framework');
- var pyUtils = require('web.py_utils');
- var Dialog = require('web.Dialog');
+ var core = require("web.core");
+ var DataExport = require("web.DataExport");
+ var framework = require("web.framework");
+ var pyUtils = require("web.py_utils");
+ var Dialog = require("web.Dialog");
var _t = core._t;
DataExport.include({
@@ -14,84 +14,71 @@ odoo.define('base_export_async.DataExport', function(require) {
A flag (checkbox) Async is added and if checked, call the
delay export instead of the standard export.
*/
- start: function() {
+ start: function () {
this._super.apply(this, arguments);
- this.async = this.$('#async_export');
+ this.async = this.$("#async_export");
},
- export_data: function() {
+ export_data: function () {
var self = this;
if (self.async.is(":checked")) {
/*
Checks from the standard method
*/
- var exported_fields = this.$(
- '.o_fields_list option').map(
- function() {
+ var exported_fields = this.$(".o_fields_list option")
+ .map(function () {
return {
- name: (self.records[this.value] ||
- this).value,
- label: this.textContent ||
- this.innerText
+ name: (self.records[this.value] || this).value,
+ label: this.textContent || this.innerText,
};
- }).get();
+ })
+ .get();
if (_.isEmpty(exported_fields)) {
- Dialog.alert(this, _t(
- "Please select fields to export..."
- ));
+ Dialog.alert(this, _t("Please select fields to export..."));
return;
}
if (!this.isCompatibleMode) {
exported_fields.unshift({
- name: 'id',
- label: _t('External ID')
+ name: "id",
+ label: _t("External ID"),
});
}
- var export_format = this.$export_format_inputs
- .filter(':checked').val();
+ var export_format = this.$export_format_inputs.filter(":checked").val();
/*
Call the delay export if Async is checked
*/
framework.blockUI();
this._rpc({
- model: 'delay.export',
- method: 'delay_export',
- args: [{
- data: JSON.stringify({
- format: export_format,
- model: this
- .record
- .model,
- fields: exported_fields,
- ids: this
- .ids_to_export,
- domain: this
- .domain,
- context: pyUtils
- .eval(
- 'contexts', [
- this
- .record
- .getContext()
- ]
- ),
- import_compat:
- !!
- this
- .$import_compat_radios
- .filter(
- ':checked'
- ).val(),
- user_ids: [this.record.context.uid],
- })
- }],
- }).then(function(result) {
+ model: "delay.export",
+ method: "delay_export",
+ args: [
+ {
+ data: JSON.stringify({
+ format: export_format,
+ model: this.record.model,
+ fields: exported_fields,
+ ids: this.ids_to_export,
+ domain: this.domain,
+ context: pyUtils.eval("contexts", [
+ this.record.getContext(),
+ ]),
+ import_compat: Boolean(this.$import_compat_radios
+ .filter(":checked")
+ .val()),
+ user_ids: [this.record.context.uid],
+ }),
+ },
+ ],
+ }).then(function (result) {
framework.unblockUI();
- Dialog.alert(this, _t(
- "You will receive the export file by email as soon as it is finished."
- ));
+ Dialog.alert(
+ this,
+ _t(
+ "You will receive the export file by email as soon as it is finished."
+ )
+ );
});
} else {
/*
diff --git a/base_export_async/static/src/xml/base.xml b/base_export_async/static/src/xml/base.xml
index 24f53a000f..387189bc7b 100644
--- a/base_export_async/static/src/xml/base.xml
+++ b/base_export_async/static/src/xml/base.xml
@@ -1,12 +1,13 @@
-
+
diff --git a/base_export_async/tests/test_base_export_async.py b/base_export_async/tests/test_base_export_async.py
index 13d377eb62..1c6e6bbd18 100644
--- a/base_export_async/tests/test_base_export_async.py
+++ b/base_export_async/tests/test_base_export_async.py
@@ -4,10 +4,10 @@
import json
import freezegun
+from dateutil.relativedelta import relativedelta
-from odoo import fields
import odoo.tests.common as common
-from dateutil.relativedelta import relativedelta
+from odoo import fields
data_csv = {
"data": """{"format": "csv", "model": "res.partner",
@@ -20,7 +20,8 @@
"context": {"lang": "en_US", "tz": "Europe/Brussels", "uid": 2},
"import_compat": false,
"user_ids": [2]
- }"""}
+ }"""
+}
data_xls = {
"data": """{"format": "xls", "model": "res.partner",
@@ -33,7 +34,8 @@
"context": {"lang": "en_US", "tz": "Europe/Brussels", "uid": 2},
"import_compat": false,
"user_ids": [2]
- }"""}
+ }"""
+}
class TestBaseExportAsync(common.TransactionCase):
@@ -43,14 +45,14 @@ def setUp(self):
self.job_obj = self.env["queue.job"]
def test_delay_export(self):
- """ Check that the call create a new JOB"""
+ """Check that the call create a new JOB"""
nbr_job = len(self.job_obj.search([]))
self.delay_export_obj.delay_export(data_csv)
new_nbr_job = len(self.job_obj.search([]))
self.assertEqual(new_nbr_job, nbr_job + 1)
def test_export_csv(self):
- """ Check that the export generate an attachment and email"""
+ """Check that the export generate an attachment and email"""
params = json.loads(data_csv.get("data"))
mails = self.env["mail.mail"].search([])
attachments = self.env["ir.attachment"].search([])
@@ -61,7 +63,7 @@ def test_export_csv(self):
self.assertEqual(new_attachment.datas_fname, "res.partner.csv")
def test_export_xls(self):
- """ Check that the export generate an attachment and email"""
+ """Check that the export generate an attachment and email"""
params = json.loads(data_xls.get("data"))
mails = self.env["mail.mail"].search([])
attachments = self.env["ir.attachment"].search([])
@@ -72,13 +74,14 @@ def test_export_xls(self):
self.assertEqual(new_attachment.datas_fname, "res.partner.xls")
def test_cron_delete(self):
- """ Check that cron delete attachment after TTL"""
+ """Check that cron delete attachment after TTL"""
params = json.loads(data_csv.get("data"))
attachments = self.env["ir.attachment"].search([])
self.delay_export_obj.export(params)
- new_attachment = self.env['ir.attachment'].search([]) - attachments
- time_to_live = self.env['ir.config_parameter'].sudo(). \
- get_param('attachment.ttl', 7)
+ new_attachment = self.env["ir.attachment"].search([]) - attachments
+ time_to_live = (
+ self.env["ir.config_parameter"].sudo().get_param("attachment.ttl", 7)
+ )
date_today = fields.Datetime.now()
date_past_ttl = date_today + relativedelta(days=int(time_to_live))
with freezegun.freeze_time(date_past_ttl):
diff --git a/base_export_async/views/assets.xml b/base_export_async/views/assets.xml
index f0a778851c..24f276ba7e 100644
--- a/base_export_async/views/assets.xml
+++ b/base_export_async/views/assets.xml
@@ -1,8 +1,15 @@
-
+
-
+
-
+
diff --git a/setup/base_export_async/odoo/addons/base_export_async b/setup/base_export_async/odoo/addons/base_export_async
new file mode 120000
index 0000000000..b0fe910af2
--- /dev/null
+++ b/setup/base_export_async/odoo/addons/base_export_async
@@ -0,0 +1 @@
+../../../../base_export_async
\ No newline at end of file
diff --git a/setup/base_export_async/setup.py b/setup/base_export_async/setup.py
new file mode 100644
index 0000000000..28c57bb640
--- /dev/null
+++ b/setup/base_export_async/setup.py
@@ -0,0 +1,6 @@
+import setuptools
+
+setuptools.setup(
+ setup_requires=['setuptools-odoo'],
+ odoo_addon=True,
+)
From 4a12bab336606e2fa373536dc74ee2a15aacb832 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=F0=9F=8C=B4Cuong=2C=20Nguyen=20Minh=20Tran=20Manh?=
Date: Mon, 30 Jan 2023 16:15:40 +0700
Subject: [PATCH 290/451] [MIG] base_export_async: Migration to 14.0
---
base_export_async/README.rst | 15 ++++----
base_export_async/__manifest__.py | 7 ++--
.../migrations/12.0.1.1.0/post-migrate.py | 23 ------------
.../migrations/12.0.1.1.0/pre-migrate.py | 9 -----
base_export_async/models/delay_export.py | 9 +----
base_export_async/security/ir_rule.xml | 2 +-
.../static/description/index.html | 9 +++--
.../static/src/js/data_export.js | 37 +++++--------------
base_export_async/static/src/xml/base.xml | 15 +++++---
.../tests/test_base_export_async.py | 12 +++++-
10 files changed, 48 insertions(+), 90 deletions(-)
delete mode 100644 base_export_async/migrations/12.0.1.1.0/post-migrate.py
delete mode 100644 base_export_async/migrations/12.0.1.1.0/pre-migrate.py
diff --git a/base_export_async/README.rst b/base_export_async/README.rst
index aeb47ac6c0..803843dc99 100644
--- a/base_export_async/README.rst
+++ b/base_export_async/README.rst
@@ -14,13 +14,13 @@ Base Export Async
: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/12.0/base_export_async
+ :target: https://github.com/OCA/queue/tree/14.0/base_export_async
:alt: OCA/queue
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
- :target: https://translation.odoo-community.org/projects/queue-12-0/queue-12-0-base_export_async
+ :target: https://translation.odoo-community.org/projects/queue-14-0/queue-14-0-base_export_async
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
- :target: https://runbot.odoo-community.org/runbot/230/12.0
+ :target: https://runbot.odoo-community.org/runbot/230/14.0
:alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5|
@@ -35,8 +35,8 @@ Standard Export can be delayed in asynchronous jobs executed in the background a
Usage
=====
-The user is presented with a new checkbox "Asynchronous export"
-in the export screen. When selected, the export is delayed in a
+The user is presented with a new checkbox "Asynchronous export"
+in the export screen. When selected, the export is delayed in a
background job.
The .csv or .xls file generated by the export will be sent by email
@@ -48,7 +48,7 @@ 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 `_.
+`feedback `_.
Do not contact contributors directly about support or help with technical issues.
@@ -65,6 +65,7 @@ Contributors
* Arnaud Pineux (ACSONE SA/NV) authored the initial prototype.
* Guewen Baconnier (Camptocamp)
+* Cuong Nguyen Minh Tran Manh (Komit)
Maintainers
~~~~~~~~~~~
@@ -79,6 +80,6 @@ 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.
+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/base_export_async/__manifest__.py b/base_export_async/__manifest__.py
index 2cb7fd87be..cfd42d9f93 100644
--- a/base_export_async/__manifest__.py
+++ b/base_export_async/__manifest__.py
@@ -3,10 +3,8 @@
{
"name": "Base Export Async",
- "summary": """
- Asynchronous export with job queue
- """,
- "version": "12.0.1.1.0",
+ "summary": "Asynchronous export with job queue",
+ "version": "14.0.1.0.0",
"license": "AGPL-3",
"author": "ACSONE SA/NV, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/queue",
@@ -20,4 +18,5 @@
],
"demo": [],
"qweb": ["static/src/xml/base.xml"],
+ "installable": True,
}
diff --git a/base_export_async/migrations/12.0.1.1.0/post-migrate.py b/base_export_async/migrations/12.0.1.1.0/post-migrate.py
deleted file mode 100644
index 6ace97b1df..0000000000
--- a/base_export_async/migrations/12.0.1.1.0/post-migrate.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Copyright 2019 Camptocamp
-# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-
-from odoo import SUPERUSER_ID, api
-from odoo.tools.sql import column_exists
-
-
-def migrate(cr, version):
- if not column_exists(cr, "delay_export", "__temp_user_id"):
- return
- with api.Environment.manage():
- env = api.Environment(cr, SUPERUSER_ID, {})
- field = env["delay.export"]._fields["user_ids"]
- rel, id1, id2 = field.relation, field.column1, field.column2
- env.cr.execute(
- """
- INSERT INTO %s (%s, %s)
- SELECT id, __temp_user_id
- FROM delay_export
- """
- % (rel, id1, id2)
- )
- env.cr.execute("ALTER TABLE delay_export DROP COLUMN __temp_user_id;")
diff --git a/base_export_async/migrations/12.0.1.1.0/pre-migrate.py b/base_export_async/migrations/12.0.1.1.0/pre-migrate.py
deleted file mode 100644
index 51ca5be7bf..0000000000
--- a/base_export_async/migrations/12.0.1.1.0/pre-migrate.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright 2019 Camptocamp
-# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-
-from odoo.tools.sql import column_exists, rename_column
-
-
-def migrate(cr, version):
- if column_exists(cr, "delay_export", "user_id"):
- rename_column(cr, "delay_export", "user_id", "__temp_user_id")
diff --git a/base_export_async/models/delay_export.py b/base_export_async/models/delay_export.py
index 6e399c9a34..c940425108 100644
--- a/base_export_async/models/delay_export.py
+++ b/base_export_async/models/delay_export.py
@@ -3,7 +3,6 @@
import base64
import json
-import logging
import operator
from dateutil.relativedelta import relativedelta
@@ -11,11 +10,8 @@
from odoo import _, api, fields, models
from odoo.exceptions import UserError
-from odoo.addons.queue_job.job import job
from odoo.addons.web.controllers.main import CSVExport, ExcelExport
-_logger = logging.getLogger(__name__)
-
class DelayExport(models.Model):
@@ -35,7 +31,6 @@ def delay_export(self, data):
@api.model
def _get_file_content(self, params):
export_format = params.get("format")
- raw_data = export_format != "csv"
items = operator.itemgetter(
"model", "fields", "ids", "domain", "import_compat", "context", "user_ids"
@@ -53,7 +48,7 @@ def _get_file_content(self, params):
fields_name = [field for field in fields_name if field["name"] != "id"]
field_names = [f["name"] for f in fields_name]
- import_data = records.export_data(field_names, raw_data).get("datas", [])
+ import_data = records.export_data(field_names).get("datas", [])
if import_compat:
columns_headers = field_names
@@ -68,7 +63,6 @@ def _get_file_content(self, params):
return xls.from_data(columns_headers, import_data)
@api.model
- @job
def export(self, params):
"""Delayed export of a file sent by email
@@ -97,7 +91,6 @@ def export(self, params):
{
"name": name,
"datas": base64.b64encode(content),
- "datas_fname": name,
"type": "binary",
"res_model": self._name,
"res_id": export_record.id,
diff --git a/base_export_async/security/ir_rule.xml b/base_export_async/security/ir_rule.xml
index d5bbd85e41..d7934f09ae 100644
--- a/base_export_async/security/ir_rule.xml
+++ b/base_export_async/security/ir_rule.xml
@@ -1,5 +1,5 @@
-
+Only user can read delay.export
diff --git a/base_export_async/static/description/index.html b/base_export_async/static/description/index.html
index a3b0940f19..9677dc1f2f 100644
--- a/base_export_async/static/description/index.html
+++ b/base_export_async/static/description/index.html
@@ -3,7 +3,7 @@
-
+
Base Export Async
+
+
+
+
Scheduled Asynchronous Export
+
+
+
+
Add a new Automation feature: Scheduled Exports.
+Based on an export list and a domain, an email is sent every X
+hours/days/weeks/months to a selection of users.
When the configuration of a Scheduled Export is done, their execution
+is automatic.
+
Users will receive an email containing a link to download the exported file at
+the specified frequency. The attachments stay in the database for 7 days by
+default (it can be changed with the system parameter attachment.ttl.
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.
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.
In earlier versions, jobs could be configured using the @job decorator.
This is now obsolete, they can be configured using optional queue.job.function
and queue.job.channel XML records.
The job should test at the very beginning its relevance: the moment the job will be executed is unknown by design. So the first task of a job should be to check if the related work is still relevant at the moment of the execution.
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
@@ -954,16 +959,16 @@
From 731b3056f864216231d5f851abe24e74e414937e Mon Sep 17 00:00:00 2001
From: Weblate
Date: Thu, 12 Jun 2025 13:43:22 +0000
Subject: [PATCH 443/451] Update translation files
Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.
Translation: queue-14.0/queue-14.0-queue_job
Translate-URL: https://translation.odoo-community.org/projects/queue-14-0/queue-14-0-queue_job/
---
queue_job/i18n/de.po | 5 +++++
queue_job/i18n/es.po | 5 +++++
queue_job/i18n/it.po | 5 +++++
queue_job/i18n/ro.po | 5 +++++
queue_job/i18n/zh_CN.po | 5 +++++
5 files changed, 25 insertions(+)
diff --git a/queue_job/i18n/de.po b/queue_job/i18n/de.po
index f0bb8cdb05..10ddd4452a 100644
--- a/queue_job/i18n/de.po
+++ b/queue_job/i18n/de.po
@@ -908,6 +908,11 @@ msgstr ""
msgid "Time required to execute this job in seconds. Average when grouped."
msgstr ""
+#. module: queue_job
+#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search
+msgid "Tried many times"
+msgstr ""
+
#. module: queue_job
#: model:ir.model.fields,help:queue_job.field_queue_job__activity_exception_decoration
msgid "Type of the exception activity on record."
diff --git a/queue_job/i18n/es.po b/queue_job/i18n/es.po
index fdeab442e1..bbe3a4b314 100644
--- a/queue_job/i18n/es.po
+++ b/queue_job/i18n/es.po
@@ -888,6 +888,11 @@ msgstr ""
msgid "Time required to execute this job in seconds. Average when grouped."
msgstr ""
+#. module: queue_job
+#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search
+msgid "Tried many times"
+msgstr ""
+
#. module: queue_job
#: model:ir.model.fields,help:queue_job.field_queue_job__activity_exception_decoration
msgid "Type of the exception activity on record."
diff --git a/queue_job/i18n/it.po b/queue_job/i18n/it.po
index ccb189184c..a659d5caa8 100644
--- a/queue_job/i18n/it.po
+++ b/queue_job/i18n/it.po
@@ -892,6 +892,11 @@ msgstr "Durata (s)"
msgid "Time required to execute this job in seconds. Average when grouped."
msgstr ""
+#. module: queue_job
+#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search
+msgid "Tried many times"
+msgstr ""
+
#. module: queue_job
#: model:ir.model.fields,help:queue_job.field_queue_job__activity_exception_decoration
msgid "Type of the exception activity on record."
diff --git a/queue_job/i18n/ro.po b/queue_job/i18n/ro.po
index c1bb27ef03..b96a9cd281 100644
--- a/queue_job/i18n/ro.po
+++ b/queue_job/i18n/ro.po
@@ -891,6 +891,11 @@ msgstr ""
msgid "Time required to execute this job in seconds. Average when grouped."
msgstr ""
+#. module: queue_job
+#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search
+msgid "Tried many times"
+msgstr ""
+
#. module: queue_job
#: model:ir.model.fields,help:queue_job.field_queue_job__activity_exception_decoration
msgid "Type of the exception activity on record."
diff --git a/queue_job/i18n/zh_CN.po b/queue_job/i18n/zh_CN.po
index 68746073b8..f4f8007f42 100644
--- a/queue_job/i18n/zh_CN.po
+++ b/queue_job/i18n/zh_CN.po
@@ -903,6 +903,11 @@ msgstr ""
msgid "Time required to execute this job in seconds. Average when grouped."
msgstr ""
+#. module: queue_job
+#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search
+msgid "Tried many times"
+msgstr ""
+
#. module: queue_job
#: model:ir.model.fields,help:queue_job.field_queue_job__activity_exception_decoration
msgid "Type of the exception activity on record."
From d522451d30abc68e745d746a3b29a8fa56d6baa8 Mon Sep 17 00:00:00 2001
From: OCA-git-bot
Date: Thu, 12 Jun 2025 13:45:51 +0000
Subject: [PATCH 444/451] [BOT] post-merge updates
---
README.md | 2 +-
queue_job/README.rst | 2 +-
queue_job/__manifest__.py | 2 +-
queue_job/static/description/index.html | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index d0f01584ef..a0d01e4ece 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,7 @@ addon | version | maintainers | summary
[base_export_async](base_export_async/) | 14.0.1.0.1 | | Asynchronous export with job queue
[base_import_async](base_import_async/) | 14.0.1.0.2 | | Import CSV files in the background
[export_async_schedule](export_async_schedule/) | 14.0.1.0.1 | | Generate and send exports by emails on a schedule
-[queue_job](queue_job/) | 14.0.3.12.0 | | Job Queue
+[queue_job](queue_job/) | 14.0.3.13.0 | | Job Queue
[queue_job_batch](queue_job_batch/) | 14.0.1.0.2 | | Job Queue Batch
[queue_job_context](queue_job_context/) | 14.0.1.0.1 | | Queue Job, prepare context before enqueue keys
[queue_job_cron](queue_job_cron/) | 14.0.2.0.0 | | Scheduled Actions as Queue Jobs
diff --git a/queue_job/README.rst b/queue_job/README.rst
index 3bd06b824a..cc9c8a29b6 100644
--- a/queue_job/README.rst
+++ b/queue_job/README.rst
@@ -11,7 +11,7 @@ Job Queue
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- !! source digest: sha256:c6f84e963de902ae65caf0f535e67f655c2bc24dbd6b6768f2b16c67195a2c43
+ !! source digest: sha256:24bc5746fee55884d7fcb4f867d8d62ab10502eaf019f69ac5b03132c6da088b
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Mature-brightgreen.png
diff --git a/queue_job/__manifest__.py b/queue_job/__manifest__.py
index b074f13c9f..2aeda46898 100644
--- a/queue_job/__manifest__.py
+++ b/queue_job/__manifest__.py
@@ -2,7 +2,7 @@
{
"name": "Job Queue",
- "version": "14.0.3.12.0",
+ "version": "14.0.3.13.0",
"author": "Camptocamp,ACSONE SA/NV,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/queue",
"license": "LGPL-3",
diff --git a/queue_job/static/description/index.html b/queue_job/static/description/index.html
index c015a8fb2d..8c57c58f00 100644
--- a/queue_job/static/description/index.html
+++ b/queue_job/static/description/index.html
@@ -372,7 +372,7 @@
Job Queue
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-!! source digest: sha256:c6f84e963de902ae65caf0f535e67f655c2bc24dbd6b6768f2b16c67195a2c43
+!! source digest: sha256:24bc5746fee55884d7fcb4f867d8d62ab10502eaf019f69ac5b03132c6da088b
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-!! source digest: sha256:24bc5746fee55884d7fcb4f867d8d62ab10502eaf019f69ac5b03132c6da088b
+!! source digest: sha256:09e503d2be6fc74fe0001d75f167ae42a219d35ff383990befca5a5248e505b9
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
This addon adds an integrated Job Queue to Odoo.
From 5803c2a43dfe65837caa5fc502d215753556fc2c Mon Sep 17 00:00:00 2001
From: Florent Xicluna
Date: Wed, 25 Jun 2025 09:24:52 +0200
Subject: [PATCH 447/451] [IMP] queue_job: add index for efficient autovacuum
---
queue_job/models/queue_job.py | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/queue_job/models/queue_job.py b/queue_job/models/queue_job.py
index 97b187b7df..5c69655181 100644
--- a/queue_job/models/queue_job.py
+++ b/queue_job/models/queue_job.py
@@ -7,7 +7,7 @@
from odoo import _, api, exceptions, fields, models
from odoo.osv import expression
-from odoo.tools import config, html_escape
+from odoo.tools import config, html_escape, index_exists
from odoo.addons.base_sparse_field.models.fields import Serialized
@@ -131,16 +131,21 @@ class QueueJob(models.Model):
worker_pid = fields.Integer(readonly=True)
def init(self):
- self._cr.execute(
- "SELECT indexname FROM pg_indexes WHERE indexname = %s ",
- ("queue_job_identity_key_state_partial_index",),
- )
- if not self._cr.fetchone():
+ index_1 = "queue_job_identity_key_state_partial_index"
+ index_2 = "queue_job_channel_date_done_date_created_index"
+ if not index_exists(self._cr, index_1):
+ # Used by Job.job_record_with_same_identity_key
self._cr.execute(
"CREATE INDEX queue_job_identity_key_state_partial_index "
"ON queue_job (identity_key) WHERE state in ('pending', "
"'enqueued', 'wait_dependencies') AND identity_key IS NOT NULL;"
)
+ if not index_exists(self._cr, index_2):
+ # Used by .autovacuum
+ self._cr.execute(
+ "CREATE INDEX queue_job_channel_date_done_date_created_index "
+ "ON queue_job (channel, date_done, date_created);"
+ )
@api.depends("records")
def _compute_record_ids(self):
@@ -396,6 +401,7 @@ def autovacuum(self):
("date_cancelled", "<=", deadline),
("channel", "=", channel.complete_name),
],
+ order="date_done, date_created",
limit=1000,
)
if jobs:
From 8b115843911bec7eec6e1c3e287e17196ae5e198 Mon Sep 17 00:00:00 2001
From: OCA-git-bot
Date: Wed, 25 Jun 2025 10:55:40 +0000
Subject: [PATCH 448/451] [BOT] post-merge updates
---
README.md | 2 +-
queue_job/README.rst | 2 +-
queue_job/__manifest__.py | 2 +-
queue_job/static/description/index.html | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index b8d7df135c..5c93781b85 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,7 @@ addon | version | maintainers | summary
[base_export_async](base_export_async/) | 14.0.1.0.1 | | Asynchronous export with job queue
[base_import_async](base_import_async/) | 14.0.1.0.2 | | Import CSV files in the background
[export_async_schedule](export_async_schedule/) | 14.0.1.0.1 | | Generate and send exports by emails on a schedule
-[queue_job](queue_job/) | 14.0.3.14.0 | | Job Queue
+[queue_job](queue_job/) | 14.0.3.15.0 | | Job Queue
[queue_job_batch](queue_job_batch/) | 14.0.1.0.2 | | Job Queue Batch
[queue_job_context](queue_job_context/) | 14.0.1.0.1 | | Queue Job, prepare context before enqueue keys
[queue_job_cron](queue_job_cron/) | 14.0.2.0.0 | | Scheduled Actions as Queue Jobs
diff --git a/queue_job/README.rst b/queue_job/README.rst
index fc5a2ca3bb..e5c372da8c 100644
--- a/queue_job/README.rst
+++ b/queue_job/README.rst
@@ -11,7 +11,7 @@ Job Queue
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- !! source digest: sha256:09e503d2be6fc74fe0001d75f167ae42a219d35ff383990befca5a5248e505b9
+ !! source digest: sha256:9f7dad42fbcb042d09ea5124a82257167c8d6bf4ebc51f97999e9031e8c7eb23
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Mature-brightgreen.png
diff --git a/queue_job/__manifest__.py b/queue_job/__manifest__.py
index af0de9d95c..28340b81e1 100644
--- a/queue_job/__manifest__.py
+++ b/queue_job/__manifest__.py
@@ -2,7 +2,7 @@
{
"name": "Job Queue",
- "version": "14.0.3.14.0",
+ "version": "14.0.3.15.0",
"author": "Camptocamp,ACSONE SA/NV,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/queue",
"license": "LGPL-3",
diff --git a/queue_job/static/description/index.html b/queue_job/static/description/index.html
index 5b5cab55a0..c0bc6ee76d 100644
--- a/queue_job/static/description/index.html
+++ b/queue_job/static/description/index.html
@@ -372,7 +372,7 @@
Job Queue
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-!! source digest: sha256:09e503d2be6fc74fe0001d75f167ae42a219d35ff383990befca5a5248e505b9
+!! source digest: sha256:9f7dad42fbcb042d09ea5124a82257167c8d6bf4ebc51f97999e9031e8c7eb23
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
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
@@ -401,22 +406,22 @@
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-!! source digest: sha256:9f7dad42fbcb042d09ea5124a82257167c8d6bf4ebc51f97999e9031e8c7eb23
+!! source digest: sha256:d7d006c41953034faf1dfee702a1887c0908c92b5744d6d3e48dfe52a7f2461b
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->