From 9711514b986b3d4cbb7fb0b8cc909e6e9a181dd0 Mon Sep 17 00:00:00 2001 From: Youssef AbouEgla Date: Fri, 17 Apr 2026 19:23:53 +0200 Subject: [PATCH 1/3] [MIG] queue_job_batch: migration to 19.0 --- .pre-commit-config.yaml | 1 - queue_job_batch/README.rst | 281 ++--- queue_job_batch/__manifest__.py | 7 +- queue_job_batch/controllers/__init__.py | 1 + queue_job_batch/controllers/main.py | 110 ++ queue_job_batch/controllers/webclient.py | 16 +- queue_job_batch/models/queue_job.py | 1 + queue_job_batch/models/res_users.py | 17 +- queue_job_batch/readme/DESCRIPTION.md | 12 +- queue_job_batch/security/security.xml | 2 +- queue_job_batch/static/description/index.html | 966 +++++++++--------- queue_job_batch/static/src/Store.esm.js | 8 - .../src/components/QueueJobBatchMenu.esm.js | 8 +- queue_job_batch/tests/__init__.py | 1 + queue_job_batch/tests/test_queue_job_batch.py | 68 ++ .../views/queue_job_batch_views.xml | 2 +- 16 files changed, 846 insertions(+), 655 deletions(-) create mode 100644 queue_job_batch/controllers/main.py create mode 100644 queue_job_batch/tests/__init__.py create mode 100644 queue_job_batch/tests/test_queue_job_batch.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4cefd00cac..373af8b05d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -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/| diff --git a/queue_job_batch/README.rst b/queue_job_batch/README.rst index d0b9d22c38..9bc3402d88 100644 --- a/queue_job_batch/README.rst +++ b/queue_job_batch/README.rst @@ -1,137 +1,144 @@ -=============== -Job Queue Batch -=============== - -.. - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! This file is generated by oca-gen-addon-readme !! - !! changes will be overwritten. !! - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:b6e605728ea05be0bf98dbe6e94c197bab21882e965286ef214f719e252b444e - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png - :target: https://odoo-community.org/page/development-status - :alt: Beta -.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png - :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html - :alt: License: AGPL-3 -.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fqueue-lightgray.png?logo=github - :target: https://github.com/OCA/queue/tree/18.0/queue_job_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 - :alt: Translate me on Weblate -.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png - :target: https://runboat.odoo-community.org/builds?repo=OCA/queue&target_branch=18.0 - :alt: Try me on Runboat - -|badge1| |badge2| |badge3| |badge4| |badge5| - -This 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:: python - - from odoo import models, fields, api - - - class MyModel(models.Model): - _name = 'my.model' - - 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') - model = self.env['my.model'].with_context(job_batch=batch) - for i in range(1, 100): - model.with_delay().my_method('a', k=i) - -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 to smash 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 - -- `Trobz `__: - - - Hoang Diep - -- `ForgeFlow `__: - - - Lois Rilo - - Jasmin Solanki - -- `Camptocamp `__: - - - Maksym Yankin - - Iván Todorovich - -Other credits -------------- - -The migration of this module from 12.0 to 14.0 was financially supported -by Camptocamp - -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. +.. 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 +=============== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:b6e605728ea05be0bf98dbe6e94c197bab21882e965286ef214f719e252b444e + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/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/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-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=19.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This addon groups related queue jobs into batches. + +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: + +.. code:: python + + from odoo import models, fields, api + + + class MyModel(models.Model): + _name = 'my.model' + + 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' + + 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) + for i in range(1, 100): + model.with_delay().my_method('a', k=i) + +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 jobs in the batch have finished, the batch is marked as done. + +**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 to smash 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 + +- `Trobz `__: + + - Hoang Diep + +- `ForgeFlow `__: + + - Lois Rilo + - Jasmin Solanki + +- `Camptocamp `__: + + - Maksym Yankin + - Iván Todorovich + +Other credits +------------- + +The migration of this module from 12.0 to 14.0 was financially supported +by Camptocamp + +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/__manifest__.py b/queue_job_batch/__manifest__.py index 12dc679b8f..f8a64d20ef 100644 --- a/queue_job_batch/__manifest__.py +++ b/queue_job_batch/__manifest__.py @@ -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", @@ -29,7 +29,6 @@ "queue_job_batch/static/src/**/*.xml", "queue_job_batch/static/src/**/*.scss", ], - 'installable': False, -}, - 'installable': False, + }, + "installable": True, } diff --git a/queue_job_batch/controllers/__init__.py b/queue_job_batch/controllers/__init__.py index eb50087f1d..6525dcae81 100644 --- a/queue_job_batch/controllers/__init__.py +++ b/queue_job_batch/controllers/__init__.py @@ -1 +1,2 @@ +from . import main from . import webclient diff --git a/queue_job_batch/controllers/main.py b/queue_job_batch/controllers/main.py new file mode 100644 index 0000000000..a16302dd1e --- /dev/null +++ b/queue_job_batch/controllers/main.py @@ -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)}" \ No newline at end of file diff --git a/queue_job_batch/controllers/webclient.py b/queue_job_batch/controllers/webclient.py index 8967932795..c4f7d3df29 100644 --- a/queue_job_batch/controllers/webclient.py +++ b/queue_job_batch/controllers/webclient.py @@ -7,17 +7,15 @@ 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): + 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 diff --git a/queue_job_batch/models/queue_job.py b/queue_job_batch/models/queue_job.py index ddc3efe879..4451df7e44 100644 --- a/queue_job_batch/models/queue_job.py +++ b/queue_job_batch/models/queue_job.py @@ -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): diff --git a/queue_job_batch/models/res_users.py b/queue_job_batch/models/res_users.py index de53d2b31f..10a5be50d6 100644 --- a/queue_job_batch/models/res_users.py +++ b/queue_job_batch/models/res_users.py @@ -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): diff --git a/queue_job_batch/readme/DESCRIPTION.md b/queue_job_batch/readme/DESCRIPTION.md index c866311ee6..a8c59f4cb0 100644 --- a/queue_job_batch/readme/DESCRIPTION.md +++ b/queue_job_batch/readme/DESCRIPTION.md @@ -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: @@ -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) @@ -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. diff --git a/queue_job_batch/security/security.xml b/queue_job_batch/security/security.xml index 9443708ae0..b6d104442f 100644 --- a/queue_job_batch/security/security.xml +++ b/queue_job_batch/security/security.xml @@ -3,7 +3,7 @@ Job Queue Batch User - + - - - - -Job Queue Batch - - - -
-

Job Queue Batch

- - -

Beta License: AGPL-3 OCA/queue Translate me on Weblate Try me on Runboat

-

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:

-
-from odoo import models, fields, api
-
-
-class MyModel(models.Model):
-    _name = 'my.model'
-
-    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')
-        model = self.env['my.model'].with_context(job_batch=batch)
-        for i in range(1, 100):
-            model.with_delay().my_method('a', k=i)
-
-

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

- -
-

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 to smash 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

- -
-
-

Other credits

-

The migration of this module from 12.0 to 14.0 was financially supported -by Camptocamp

-
-
-

Maintainers

-

This module is maintained by the OCA.

- -Odoo Community Association - -

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.

-
-
-
- - + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Job Queue Batch

+ +

Beta License: AGPL-3 OCA/queue Translate me on Weblate Try me on Runboat

+

This addon groups related queue jobs into batches.

+

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:

+
+from odoo import models, fields, api
+
+
+class MyModel(models.Model):
+    _name = 'my.model'
+
+    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'
+
+    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)
+        for i in range(1, 100):
+            model.with_delay().my_method('a', k=i)
+
+

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 jobs in the batch have finished, the batch is marked as done.

+

Table of contents

+ +
+

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 to smash 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

+ +
+
+

Other credits

+

The migration of this module from 12.0 to 14.0 was financially supported +by Camptocamp

+
+
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

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/static/src/Store.esm.js b/queue_job_batch/static/src/Store.esm.js index 619d7090b0..e8d2e47076 100644 --- a/queue_job_batch/static/src/Store.esm.js +++ b/queue_job_batch/static/src/Store.esm.js @@ -11,12 +11,4 @@ patch(Store.prototype, { hasQueueJobBatchUserGroup: false, queueJobBatchCounterBusId: 0, queueJobBatchCounter: 0, - - /** @override */ - get initMessagingParams() { - return { - ...super.initMessagingParams, - systray_get_queue_job_batches: true, - }; - }, }); diff --git a/queue_job_batch/static/src/components/QueueJobBatchMenu.esm.js b/queue_job_batch/static/src/components/QueueJobBatchMenu.esm.js index a8addd461b..a305b99467 100644 --- a/queue_job_batch/static/src/components/QueueJobBatchMenu.esm.js +++ b/queue_job_batch/static/src/components/QueueJobBatchMenu.esm.js @@ -3,7 +3,7 @@ License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */ -import {Component, useState} from "@odoo/owl"; +import {Component} from "@odoo/owl"; import {ColumnProgress} from "@web/views/view_components/column_progress"; import {Dropdown} from "@web/core/dropdown/dropdown"; import {registry} from "@web/core/registry"; @@ -24,15 +24,15 @@ export class QueueJobBatchMenu extends Component { setup() { super.setup(); this.discussSystray = useDiscussSystray(); - this.store = useState(useService("mail.store")); + this.store = useService("mail.store"); this.action = useService("action"); this.userId = user.userId; - this.ui = useState(useService("ui")); + this.ui = useService("ui"); this.dropdown = useDropdownState(); } onBeforeOpen() { - this.store.fetchData({systray_get_queue_job_batches: true}); + return this.store.fetchStoreData("systray_get_queue_job_batches"); } getGroupInfo(batch) { diff --git a/queue_job_batch/tests/__init__.py b/queue_job_batch/tests/__init__.py new file mode 100644 index 0000000000..39cec46423 --- /dev/null +++ b/queue_job_batch/tests/__init__.py @@ -0,0 +1 @@ +from . import test_queue_job_batch diff --git a/queue_job_batch/tests/test_queue_job_batch.py b/queue_job_batch/tests/test_queue_job_batch.py new file mode 100644 index 0000000000..fca559a3ed --- /dev/null +++ b/queue_job_batch/tests/test_queue_job_batch.py @@ -0,0 +1,68 @@ +# Copyright 2026 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo.tests import common +from odoo.tools import mute_logger + +from odoo.addons.mail.tools.discuss import Store + +from ..controllers.main import RunJobBatchController + + +class TestQueueJobBatchStoreData(common.TransactionCase): + def test_init_store_data_sets_batch_globals(self): + batch_group = self.env.ref("queue_job_batch.group_queue_job_batch_user") + self.env.user.write({"group_ids": [(4, batch_group.id)]}) + self.env["queue.job.batch"].sudo().create( + { + "name": "Batch", + "user_id": self.env.user.id, + "company_id": self.env.company.id, + "is_read": False, + } + ) + + store = Store() + self.env["res.users"]._init_store_data(store) + store_data = store.get_result()["Store"] + + self.assertTrue(store_data["hasQueueJobBatchUserGroup"]) + self.assertEqual(store_data["queueJobBatchCounter"], 1) + self.assertIn("queueJobBatchCounterBusId", store_data) + + +class TestQueueJobBatchCreatePrivate(common.HttpCase): + def test_queue_job_create_stays_private(self): + self.authenticate("admin", "admin") + with self.assertRaises(common.JsonRpcException) as cm, mute_logger("odoo.http"): + self.make_jsonrpc_request( + "/web/dataset/call_kw", + params={ + "model": "queue.job", + "method": "create", + "args": [], + "kwargs": { + "method_name": "write", + "model_name": "res.partner", + "uuid": "test", + }, + }, + ) + self.assertEqual("odoo.exceptions.AccessError", str(cm.exception)) + + +class TestQueueJobBatchController(common.TransactionCase): + def test_create_test_batch_jobs_links_jobs_to_batch(self): + batch, job_uuids = RunJobBatchController._create_test_batch_jobs( + self.env, + size=3, + description="Batch smoke test", + batch_name="Batch smoke test", + ) + + jobs = self.env["queue.job"].search([("uuid", "in", job_uuids)]) + + self.assertEqual(3, len(job_uuids)) + self.assertEqual(3, len(jobs)) + self.assertEqual(3, batch.job_count) + self.assertEqual({batch.id}, set(jobs.mapped("job_batch_id").ids)) diff --git a/queue_job_batch/views/queue_job_batch_views.xml b/queue_job_batch/views/queue_job_batch_views.xml index 34bdc1fee3..3c4e00a84c 100644 --- a/queue_job_batch/views/queue_job_batch_views.xml +++ b/queue_job_batch/views/queue_job_batch_views.xml @@ -96,7 +96,7 @@ string="Finished" domain="[('state', '=', 'finished')]" /> - + Date: Fri, 17 Apr 2026 19:23:53 +0200 Subject: [PATCH 2/3] [FIX] queue_job_batch: finalize tests and CI cleanup --- queue_job_batch/README.rst | 288 +++++++++--------- queue_job_batch/controllers/main.py | 220 ++++++------- queue_job_batch/controllers/webclient.py | 3 +- .../src/components/QueueJobBatchMenu.esm.js | 4 +- queue_job_batch/tests/test_queue_job_batch.py | 142 +++++++++ 5 files changed, 400 insertions(+), 257 deletions(-) diff --git a/queue_job_batch/README.rst b/queue_job_batch/README.rst index 9bc3402d88..fd3129178b 100644 --- a/queue_job_batch/README.rst +++ b/queue_job_batch/README.rst @@ -1,144 +1,144 @@ -.. 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 -=============== - -.. - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! This file is generated by oca-gen-addon-readme !! - !! changes will be overwritten. !! - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:b6e605728ea05be0bf98dbe6e94c197bab21882e965286ef214f719e252b444e - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -.. |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/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/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-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=19.0 - :alt: Try me on Runboat - -|badge1| |badge2| |badge3| |badge4| |badge5| - -This addon groups related queue jobs into batches. - -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: - -.. code:: python - - from odoo import models, fields, api - - - class MyModel(models.Model): - _name = 'my.model' - - 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' - - 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) - for i in range(1, 100): - model.with_delay().my_method('a', k=i) - -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 jobs in the batch have finished, the batch is marked as done. - -**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 to smash 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 - -- `Trobz `__: - - - Hoang Diep - -- `ForgeFlow `__: - - - Lois Rilo - - Jasmin Solanki - -- `Camptocamp `__: - - - Maksym Yankin - - Iván Todorovich - -Other credits -------------- - -The migration of this module from 12.0 to 14.0 was financially supported -by Camptocamp - -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. +.. 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 +=============== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:b6e605728ea05be0bf98dbe6e94c197bab21882e965286ef214f719e252b444e + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/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/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-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=19.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This addon groups related queue jobs into batches. + +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: + +.. code:: python + + from odoo import models, fields, api + + + class MyModel(models.Model): + _name = 'my.model' + + 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' + + 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) + for i in range(1, 100): + model.with_delay().my_method('a', k=i) + +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 jobs in the batch have finished, the batch is marked as done. + +**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 to smash 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 + +- `Trobz `__: + + - Hoang Diep + +- `ForgeFlow `__: + + - Lois Rilo + - Jasmin Solanki + +- `Camptocamp `__: + + - Maksym Yankin + - Iván Todorovich + +Other credits +------------- + +The migration of this module from 12.0 to 14.0 was financially supported +by Camptocamp + +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/controllers/main.py b/queue_job_batch/controllers/main.py index a16302dd1e..400e5a242c 100644 --- a/queue_job_batch/controllers/main.py +++ b/queue_job_batch/controllers/main.py @@ -1,110 +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)}" \ No newline at end of file +# 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)}" diff --git a/queue_job_batch/controllers/webclient.py b/queue_job_batch/controllers/webclient.py index c4f7d3df29..28e08aed52 100644 --- a/queue_job_batch/controllers/webclient.py +++ b/queue_job_batch/controllers/webclient.py @@ -9,7 +9,7 @@ class WebClient(WebclientController): @classmethod def _process_request_for_internal_user(cls, store, name, params): - super()._process_request_for_internal_user(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() @@ -19,3 +19,4 @@ def _process_request_for_internal_user(cls, store, name, params): queueJobBatchCounter=len(batches), queueJobBatchCounterBusId=bus_last_id, ) + return result diff --git a/queue_job_batch/static/src/components/QueueJobBatchMenu.esm.js b/queue_job_batch/static/src/components/QueueJobBatchMenu.esm.js index a305b99467..6ca831d555 100644 --- a/queue_job_batch/static/src/components/QueueJobBatchMenu.esm.js +++ b/queue_job_batch/static/src/components/QueueJobBatchMenu.esm.js @@ -3,15 +3,15 @@ License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */ -import {Component} from "@odoo/owl"; import {ColumnProgress} from "@web/views/view_components/column_progress"; +import {Component} from "@odoo/owl"; import {Dropdown} from "@web/core/dropdown/dropdown"; +import {_t} from "@web/core/l10n/translation"; import {registry} from "@web/core/registry"; import {useDiscussSystray} from "@mail/utils/common/hooks"; import {useDropdownState} from "@web/core/dropdown/dropdown_hooks"; import {useService} from "@web/core/utils/hooks"; import {user} from "@web/core/user"; -import {_t} from "@web/core/l10n/translation"; export class QueueJobBatchMenu extends Component { static template = "queue_job_batch.QueueJobBatchMenu"; diff --git a/queue_job_batch/tests/test_queue_job_batch.py b/queue_job_batch/tests/test_queue_job_batch.py index fca559a3ed..06ae760443 100644 --- a/queue_job_batch/tests/test_queue_job_batch.py +++ b/queue_job_batch/tests/test_queue_job_batch.py @@ -1,12 +1,18 @@ # Copyright 2026 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from types import SimpleNamespace +from unittest import mock + from odoo.tests import common from odoo.tools import mute_logger from odoo.addons.mail.tools.discuss import Store +from ..controllers import main as batch_main +from ..controllers import webclient as batch_webclient from ..controllers.main import RunJobBatchController +from ..controllers.webclient import WebClient class TestQueueJobBatchStoreData(common.TransactionCase): @@ -66,3 +72,139 @@ def test_create_test_batch_jobs_links_jobs_to_batch(self): self.assertEqual(3, len(jobs)) self.assertEqual(3, batch.job_count) self.assertEqual({batch.id}, set(jobs.mapped("job_batch_id").ids)) + + def test_parse_helpers_fallback_to_defaults(self): + self.assertEqual(7, RunJobBatchController._parse_int("7", 0)) + self.assertEqual(3, RunJobBatchController._parse_int(None, 3)) + self.assertEqual(3, RunJobBatchController._parse_int("bad", 3)) + self.assertEqual(1.5, RunJobBatchController._parse_float("1.5", 0)) + self.assertEqual(2.5, RunJobBatchController._parse_float(None, 2.5)) + self.assertEqual(2.5, RunJobBatchController._parse_float("bad", 2.5)) + + def test_create_test_batch_rejects_non_managers(self): + env = SimpleNamespace( + user=SimpleNamespace(has_group=lambda group: False), + _=lambda msg: msg, + ) + + with mock.patch.object(batch_main.http, "request", SimpleNamespace(env=env)): + with self.assertRaises(batch_main.Forbidden): + RunJobBatchController().create_test_batch() + + def test_create_test_batch_validates_failure_rate(self): + env = SimpleNamespace( + user=SimpleNamespace(has_group=lambda group: True), + _=lambda msg: msg, + ) + + with mock.patch.object(batch_main.http, "request", SimpleNamespace(env=env)): + with self.assertRaises(batch_main.BadRequest): + RunJobBatchController().create_test_batch(failure_rate=2) + + def test_create_test_batch_returns_empty_for_non_positive_size(self): + env = SimpleNamespace( + user=SimpleNamespace(has_group=lambda group: True), + _=lambda msg: msg, + ) + + with mock.patch.object(batch_main.http, "request", SimpleNamespace(env=env)): + result = RunJobBatchController().create_test_batch(size=0) + + self.assertEqual(200, result.status_code) + self.assertEqual("", result.get_data(as_text=True)) + + def test_create_test_batch_returns_batch_summary(self): + env = SimpleNamespace( + user=SimpleNamespace(has_group=lambda group: True), + _=lambda msg: msg, + ) + fake_batch = SimpleNamespace(id=42) + + with mock.patch.object(batch_main.http, "request", SimpleNamespace(env=env)): + with mock.patch.object( + RunJobBatchController, + "_create_test_batch_jobs", + return_value=(fake_batch, ["a", "b"]), + ) as create_jobs: + result = RunJobBatchController().create_test_batch( + size="2", + priority="3", + max_retries="4", + failure_rate="0.5", + job_duration="1.25", + failure_retry_seconds="9", + description="Smoke", + batch_name="Batch smoke", + ) + + self.assertEqual(200, result.status_code) + self.assertEqual("batch id: 42, jobs: 2", result.get_data(as_text=True)) + create_jobs.assert_called_once_with( + env, + size=2, + priority=3, + max_retries=4, + channel=None, + description="Smoke", + batch_name="Batch smoke", + failure_rate=0.5, + job_duration=1.25, + commit_within_job=False, + failure_retry_seconds=9, + ) + + +class TestQueueJobBatchWebclient(common.TransactionCase): + def test_process_request_for_internal_user_adds_batch_store_values(self): + class FakeEnv(dict): + pass + + fake_batches = [SimpleNamespace(id=1), SimpleNamespace(id=2)] + fake_user = SimpleNamespace(_get_queue_job_batches=lambda: fake_batches) + fake_bus = SimpleNamespace( + sudo=lambda: SimpleNamespace(_bus_last_id=lambda: 99) + ) + fake_env = FakeEnv({"bus.bus": fake_bus}) + fake_env.user = fake_user + fake_request = SimpleNamespace(env=fake_env) + store = mock.Mock() + + with mock.patch.object( + batch_webclient.WebclientController, + "_process_request_for_internal_user", + return_value="parent-result", + ) as super_call: + with mock.patch.object(batch_webclient, "request", fake_request): + result = WebClient._process_request_for_internal_user( + store, + "systray_get_queue_job_batches", + {}, + ) + + self.assertEqual("parent-result", result) + super_call.assert_called_once_with(store, "systray_get_queue_job_batches", {}) + store.add.assert_called_once_with(fake_batches) + store.add_global_values.assert_called_once_with( + queueJobBatchCounter=2, + queueJobBatchCounterBusId=99, + ) + + def test_process_request_for_internal_user_skips_non_batch_requests(self): + store = mock.Mock() + + with mock.patch.object( + batch_webclient.WebclientController, + "_process_request_for_internal_user", + return_value="parent-result", + ) as super_call: + with mock.patch.object(batch_webclient, "request", mock.Mock()): + result = WebClient._process_request_for_internal_user( + store, + "other_request", + {}, + ) + + self.assertEqual("parent-result", result) + super_call.assert_called_once_with(store, "other_request", {}) + store.add.assert_not_called() + store.add_global_values.assert_not_called() From e1bf0b2a07dd88d6c82001c5798569bcbe609ec2 Mon Sep 17 00:00:00 2001 From: Youssef AbouEgla Date: Fri, 17 Apr 2026 19:23:54 +0200 Subject: [PATCH 3/3] [DOC] queue_job_batch: add Youssef Egla to contributors --- queue_job_batch/README.rst | 2 ++ queue_job_batch/readme/CONTRIBUTORS.md | 6 ++++-- queue_job_batch/static/description/index.html | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/queue_job_batch/README.rst b/queue_job_batch/README.rst index fd3129178b..79928be8c2 100644 --- a/queue_job_batch/README.rst +++ b/queue_job_batch/README.rst @@ -120,6 +120,8 @@ Contributors - Maksym Yankin - Iván Todorovich +- Youssef Egla + Other credits ------------- diff --git a/queue_job_batch/readme/CONTRIBUTORS.md b/queue_job_batch/readme/CONTRIBUTORS.md index 6b13c0aa12..b4fe2e7d04 100644 --- a/queue_job_batch/readme/CONTRIBUTORS.md +++ b/queue_job_batch/readme/CONTRIBUTORS.md @@ -1,12 +1,14 @@ - Enric Tobella \<\> -- [Trobz](https://trobz.com): +- [Trobz](https://trobz.com): - Hoang Diep \<\> -- [ForgeFlow](https://forgeflow.com): +- [ForgeFlow](https://forgeflow.com): - Lois Rilo \<\> - Jasmin Solanki \<\> - [Camptocamp](https://camptocamp.com): - Maksym Yankin \<\> - Iván Todorovich \<\> + +- Youssef Egla \<\> diff --git a/queue_job_batch/static/description/index.html b/queue_job_batch/static/description/index.html index ec07ba0221..c7fd675653 100644 --- a/queue_job_batch/static/description/index.html +++ b/queue_job_batch/static/description/index.html @@ -461,6 +461,7 @@

Contributors

  • Iván Todorovich <ivan.todorovich@camptocamp.com>
  • +
  • Youssef Egla <youssefegla@gmail.com>