Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ exclude: |
(?x)
# NOT INSTALLABLE ADDONS
^base_import_async/|
^queue_job_batch/|
^queue_job_cron/|
^queue_job_cron_jobrunner/|
^queue_job_subscribe/|
Expand Down
31 changes: 20 additions & 11 deletions queue_job_batch/README.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association

===============
Job Queue Batch
===============
Expand All @@ -13,25 +17,29 @@ Job Queue Batch
.. |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
.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fqueue-lightgray.png?logo=github
:target: https://github.com/OCA/queue/tree/18.0/queue_job_batch
:target: https://github.com/OCA/queue/tree/19.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-18-0/queue-18-0-queue_job_batch
:target: https://translation.odoo-community.org/projects/queue-19-0/queue-19-0-queue_job_batch
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/queue&target_branch=18.0
:target: https://runboat.odoo-community.org/builds?repo=OCA/queue&target_branch=19.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

This addon adds an a grouper for queue jobs.
This addon groups related queue jobs into batches.

It allows to show your jobs in a batched form in order to know better
the results.
It lets users monitor a set of queued jobs as a single batch so the
overall execution state is easier to follow.

Create a batch with ``queue.job.batch.get_new_batch(...)``, pass it in
the context as ``job_batch``, and enqueue jobs with ``with_delay()`` as
usual.

Example:

Expand All @@ -50,7 +58,6 @@ Example:
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')
model = self.env['my.model'].with_context(job_batch=batch)
Expand All @@ -62,7 +69,7 @@ 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.
Once all jobs in the batch have finished, the batch is marked as done.

**Table of contents**

Expand All @@ -82,7 +89,7 @@ Bug Tracker
Bugs are tracked on `GitHub Issues <https://github.com/OCA/queue/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/queue/issues/new?body=module:%20queue_job_batch%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
`feedback <https://github.com/OCA/queue/issues/new?body=module:%20queue_job_batch%0Aversion:%2019.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

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

Expand Down Expand Up @@ -113,6 +120,8 @@ Contributors
- Maksym Yankin <maksym.yankin@camptocamp.com>
- Iván Todorovich <ivan.todorovich@camptocamp.com>

- Youssef Egla <youssefegla@gmail.com>

Other credits
-------------

Expand All @@ -132,6 +141,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 <https://github.com/OCA/queue/tree/18.0/queue_job_batch>`_ project on GitHub.
This module is part of the `OCA/queue <https://github.com/OCA/queue/tree/19.0/queue_job_batch>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
7 changes: 3 additions & 4 deletions queue_job_batch/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

{
"name": "Job Queue Batch",
"version": "18.0.1.0.0",
"version": "19.0.1.0.0",
"author": "Creu Blanca,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/queue",
"license": "AGPL-3",
Expand All @@ -29,7 +29,6 @@
"queue_job_batch/static/src/**/*.xml",
"queue_job_batch/static/src/**/*.scss",
],
'installable': False,
},
'installable': False,
},
"installable": True,
}
1 change: 1 addition & 0 deletions queue_job_batch/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from . import main
from . import webclient
110 changes: 110 additions & 0 deletions queue_job_batch/controllers/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Copyright 2026 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from werkzeug.exceptions import BadRequest, Forbidden

from odoo import http

from odoo.addons.queue_job.controllers.main import RunJobController


class RunJobBatchController(RunJobController):
@staticmethod
def _parse_int(value, default):
if value is None:
return default
try:
return int(value)
except (ValueError, TypeError):
return default

@staticmethod
def _parse_float(value, default):
if value is None:
return default
try:
return float(value)
except (ValueError, TypeError):
return default

@classmethod
def _create_test_batch_jobs(
cls,
env,
size,
priority=None,
max_retries=None,
channel=None,
description="Test job",
batch_name=None,
failure_rate=0,
job_duration=0,
commit_within_job=False,
failure_retry_seconds=0,
):
batch = env["queue.job.batch"].get_new_batch(batch_name or description)
delayed_model = env["queue.job"].with_context(job_batch=batch)
job_uuids = []

for index in range(1, size + 1):
job_description = description if size == 1 else f"{description} #{index}"
delayed = delayed_model.with_delay(
priority=priority,
max_retries=max_retries,
channel=channel,
description=job_description,
)._test_job(
failure_rate=failure_rate,
job_duration=job_duration,
commit_within_job=commit_within_job,
failure_retry_seconds=failure_retry_seconds,
)
job_uuids.append(delayed.db_record().uuid)

batch.check_state()
return batch, job_uuids

@http.route("/queue_job/create_test_batch", type="http", auth="user")
def create_test_batch(
self,
priority=None,
max_retries=None,
channel=None,
description="Test job",
batch_name=None,
size=1,
failure_rate=0,
job_duration=0,
commit_within_job=False,
failure_retry_seconds=0,
):
if not http.request.env.user.has_group("base.group_erp_manager"):
raise Forbidden(http.request.env._("Access Denied"))

failure_rate = self._parse_float(failure_rate, 0)
job_duration = self._parse_float(job_duration, 0)
if not 0 <= failure_rate <= 1:
raise BadRequest("failure_rate must be between 0 and 1")

size = self._parse_int(size, 1)
priority = self._parse_int(priority, None)
max_retries = self._parse_int(max_retries, None)
failure_retry_seconds = self._parse_int(failure_retry_seconds, 0)

if size < 1:
return ""

batch, job_uuids = self._create_test_batch_jobs(
http.request.env,
size=size,
priority=priority,
max_retries=max_retries,
channel=channel,
description=description,
batch_name=batch_name,
failure_rate=failure_rate,
job_duration=job_duration,
commit_within_job=commit_within_job,
failure_retry_seconds=failure_retry_seconds,
)
return f"batch id: {batch.id}, jobs: {len(job_uuids)}"
17 changes: 8 additions & 9 deletions queue_job_batch/controllers/webclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,16 @@


class WebClient(WebclientController):
def _process_request_for_internal_user(self, store, **kwargs):
res = super()._process_request_for_internal_user(store, **kwargs)
if kwargs.get("systray_get_queue_job_batches"):
@classmethod
def _process_request_for_internal_user(cls, store, name, params):
result = super()._process_request_for_internal_user(store, name, params)
if name == "systray_get_queue_job_batches":
# sudo: bus.bus: reading non-sensitive last id
bus_last_id = request.env["bus.bus"].sudo()._bus_last_id()
batches = request.env.user._get_queue_job_batches()
store.add(batches)
store.add(
{
"queueJobBatchCounter": len(batches),
"queueJobBatchCounterBusId": bus_last_id,
}
store.add_global_values(
queueJobBatchCounter=len(batches),
queueJobBatchCounterBusId=bus_last_id,
)
return res
return result
1 change: 1 addition & 0 deletions queue_job_batch/models/queue_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class QueueJob(models.Model):
job_batch_id = fields.Many2one("queue.job.batch")

@api.model_create_multi
@api.private
def create(self, vals_list):
batch = self.env.context.get("job_batch")
if batch and isinstance(batch, models.Model):
Expand Down
17 changes: 11 additions & 6 deletions queue_job_batch/models/res_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,18 @@ class Users(models.Model):

def _init_store_data(self, store):
res = super()._init_store_data(store)
store.add(
{
"hasQueueJobBatchUserGroup": self.env.user.has_group(
"queue_job_batch.group_queue_job_batch_user"
),
}
has_batch_group = self.env.user.has_group(
"queue_job_batch.group_queue_job_batch_user"
)
values = {"hasQueueJobBatchUserGroup": has_batch_group}
if has_batch_group and self.env.user._is_internal():
batches = self.env.user._get_queue_job_batches()
# sudo: bus.bus: reading non-sensitive last id
values.update(
queueJobBatchCounter=len(batches),
queueJobBatchCounterBusId=self.env["bus.bus"].sudo()._bus_last_id(),
)
store.add_global_values(**values)
return res

def _get_queue_job_batches(self):
Expand Down
6 changes: 4 additions & 2 deletions queue_job_batch/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
- Enric Tobella \<<etobella@creublanca.es>\>

- [Trobz](https://trobz.com):
- [Trobz](https://trobz.com):
- Hoang Diep \<<hoang@trobz.com>\>

- [ForgeFlow](https://forgeflow.com):
- [ForgeFlow](https://forgeflow.com):
- Lois Rilo \<<lois.rilo@forgeflow.com>\>
- Jasmin Solanki \<<jasmin.solanki@forgeflow.com>\>

- [Camptocamp](https://camptocamp.com):
- Maksym Yankin \<<maksym.yankin@camptocamp.com>\>
- Iván Todorovich \<<ivan.todorovich@camptocamp.com>\>

- Youssef Egla \<<youssefegla@gmail.com>\>
12 changes: 7 additions & 5 deletions queue_job_batch/readme/DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
This addon adds an a grouper for queue jobs.
This addon groups related queue jobs into batches.

It allows to show your jobs in a batched form in order to know better
the results.
It lets users monitor a set of queued jobs as a single batch so the
overall execution state is easier to follow.

Create a batch with `queue.job.batch.get_new_batch(...)`, pass it in the
context as `job_batch`, and enqueue jobs with `with_delay()` as usual.

Example:

Expand All @@ -19,7 +22,6 @@ class MyModel(models.Model):
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')
model = self.env['my.model'].with_context(job_batch=batch)
Expand All @@ -32,4 +34,4 @@ 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.
Once all jobs in the batch have finished, the batch is marked as done.
2 changes: 1 addition & 1 deletion queue_job_batch/security/security.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<data>
<record id="group_queue_job_batch_user" model="res.groups">
<field name="name">Job Queue Batch User</field>
<field name="category_id" ref="queue_job.module_category_queue_job" />
<field name="privilege_id" ref="queue_job.privilege_queue_job" />
</record>
<record id="queue_job.group_queue_job_manager" model="res.groups">
<field
Expand Down
Loading
Loading