Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
d48b29f
[ADD] queue_job_batch
etobella Jul 9, 2019
53ef01b
[11.0][FIX] queue_job_batch: view all in the systray not working
LoisRForgeFlow Jul 10, 2019
196b897
[11.0][FIX] queue_job_batch:
LoisRForgeFlow Jul 12, 2019
564db17
[MIG] queue_job_batch, test_queue_job_batch: Migration to 12.0
LoisRForgeFlow Oct 31, 2019
0144e1d
[12.0] [FIX] Allow users from group queue_job_batch_user to write on …
qgroulard Jan 2, 2020
11141d0
Added translation using Weblate (French)
ypapouin Nov 20, 2020
f1f7e6e
[IMP] queue_job_batch, test_queue_job_batch: black, isort, prettier
anothingguy Dec 3, 2021
f25e1bd
[14.0][MIG] queue_job_batch, test_queue_job_batch: Migration to 14.0
anothingguy Dec 3, 2021
c1786ee
[MIG] queue_job_batch, test_queue_job_batch: Migration to 15.0
amh-mw Jan 22, 2023
e1d0fb1
[IMP] queue_job_batch, test_queue_job_batch: black, isort, prettier
JasminSForgeFlow Nov 1, 2023
64befc2
[MIG] queue_job_batch, test_queue_job_batch: Migration to 16.0
JasminSForgeFlow Nov 9, 2023
9995386
Added translation using Weblate (Spanish)
Ivorra78 Dec 1, 2023
dab33fa
Added translation using Weblate (Italian)
mymage Jan 9, 2024
5cb212e
[IMP] queue_job_batch: Fix systray icon visibility for allowed group …
JasminSForgeFlow Mar 18, 2024
c2b7ee6
[IMP] queue_job_batch, test_queue_job_batch: black, isort, prettier
anothingguy Dec 3, 2021
5e45eb1
[14.0][MIG] queue_job_batch, test_queue_job_batch: Migration to 14.0
anothingguy Dec 3, 2021
1982009
[MIG] queue_job_batch, test_queue_job_batch: Migration to 15.0
amh-mw Jan 22, 2023
30354ba
[IMP] queue_job_batch, test_queue_job_batch: black, isort, prettier
JasminSForgeFlow Nov 1, 2023
ef9ca9a
[MIG] queue_job_batch, test_queue_job_batch: Migration to 16.0
JasminSForgeFlow Nov 9, 2023
0268593
Added translation using Weblate (Italian)
mymage Jan 9, 2024
6c76dd5
[IMP] queue_job_batch, test_queue_job_batch: pre-commit auto fixes
yankinmax Jan 8, 2025
752c650
[MIG] queue_job_batch, test_queue_job_batch: Migration to 18.0
yankinmax Jan 8, 2025
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
33 changes: 17 additions & 16 deletions queue_job_batch/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,13 @@ Example:
.. code:: python

from odoo import models, fields, api
from odoo.addons.queue_job.job import job


class MyModel(models.Model):
_name = 'my.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)
def my_method(self, a, k=None):
_logger.info('executed with a: %s and k: %s', a, k)


class MyOtherModel(models.Model):
Expand All @@ -55,11 +53,9 @@ Example:
@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):
self.env['my.model'].with_context(
job_batch=batch
).with_delay().my_method('a', k=i)
batch.enqueue()
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
Expand Down Expand Up @@ -101,16 +97,21 @@ Authors
Contributors
------------

- Enric Tobella <[email protected]>
- Enric Tobella <[email protected]>

- `Trobz <https://trobz.com>`__:

- Hoang Diep <[email protected]>

- `Trobz <https://trobz.com>`__:
- `ForgeFlow <https://forgeflow.com>`__:

- Hoang Diep <[email protected]>
- Lois Rilo <[email protected]>
- Jasmin Solanki <[email protected]>

- `ForgeFlow <https://forgeflow.com>`__:
- `Camptocamp <https://camptocamp.com>`__:

- Lois Rilo <lois.rilo@forgeflow.com>
- Jasmin Solanki <jasmin.solanki@forgeflow.com>
- Maksym Yankin <maksym.yankin@camptocamp.com>
- Iván Todorovich <ivan.todorovich@camptocamp.com>

Other credits
-------------
Expand Down
1 change: 1 addition & 0 deletions queue_job_batch/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from . import controllers
from . import models
8 changes: 4 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": "16.0.1.0.1",
"version": "18.0.1.0.0",
"author": "Creu Blanca,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/queue",
"license": "AGPL-3",
Expand All @@ -25,9 +25,9 @@
],
"assets": {
"web.assets_backend": [
"queue_job_batch/static/src/js/*.js",
"queue_job_batch/static/src/scss/systray.scss",
"queue_job_batch/static/src/xml/*.xml",
"queue_job_batch/static/src/**/*.js",
"queue_job_batch/static/src/**/*.xml",
"queue_job_batch/static/src/**/*.scss",
],
},
}
1 change: 1 addition & 0 deletions queue_job_batch/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import webclient
23 changes: 23 additions & 0 deletions queue_job_batch/controllers/webclient.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2025 Camptocamp SA (https://www.camptocamp.com).
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo.http import request

from odoo.addons.mail.controllers.webclient import WebclientController


class WebClient(WebclientController):
def _process_request_for_internal_user(self, store, **kwargs):
res = super()._process_request_for_internal_user(store, **kwargs)

Check warning on line 11 in queue_job_batch/controllers/webclient.py

View check run for this annotation

Codecov / codecov/patch

queue_job_batch/controllers/webclient.py#L11

Added line #L11 was not covered by tests
if kwargs.get("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(

Check warning on line 17 in queue_job_batch/controllers/webclient.py

View check run for this annotation

Codecov / codecov/patch

queue_job_batch/controllers/webclient.py#L14-L17

Added lines #L14 - L17 were not covered by tests
{
"queueJobBatchCounter": len(batches),
"queueJobBatchCounterBusId": bus_last_id,
}
)
return res

Check warning on line 23 in queue_job_batch/controllers/webclient.py

View check run for this annotation

Codecov / codecov/patch

queue_job_batch/controllers/webclient.py#L23

Added line #L23 was not covered by tests
19 changes: 19 additions & 0 deletions queue_job_batch/migrations/18.0.1.0.0/pre-migrate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright 2025 Camptocamp SA (https://www.camptocamp.com).
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).


def migrate(cr, version):
cr.execute(
"""
UPDATE queue_job_batch
SET state = 'pending'
WHERE state = 'draft'
"""
)
cr.execute(
"""
UPDATE queue_job_batch
SET is_read = FALSE
WHERE state != 'finished' AND is_read
"""
)
4 changes: 3 additions & 1 deletion queue_job_batch/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
from . import queue_job, queue_job_batch
from . import queue_job_batch
from . import queue_job
from . import res_users
34 changes: 17 additions & 17 deletions queue_job_batch/models/queue_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,30 @@

from odoo import api, fields, models

from odoo.addons.queue_job.job import identity_exact


class QueueJob(models.Model):
_inherit = "queue.job"

job_batch_id = fields.Many2one("queue.job.batch")

@api.model
def create(self, vals):
@api.model_create_multi
def create(self, vals_list):
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)
if batch and isinstance(batch, models.Model):
for vals in vals_list:
vals.update({"job_batch_id": batch.id})
return super().create(vals_list)

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()
if vals.get("state", "") == "done":
batches = self.env["queue.job.batch"]
for record in self:
if record.state != "done" and record.job_batch_id:
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(identity_key=identity_exact).check_state()
return super().write(vals)
101 changes: 53 additions & 48 deletions queue_job_batch/models/queue_job_batch.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

from odoo import api, fields, models

from odoo.addons.mail.tools.discuss import Store


class QueueJobBatch(models.Model):
_name = "queue.job.batch"
Expand Down Expand Up @@ -32,12 +34,12 @@
)
state = fields.Selection(
[
("draft", "Draft"),
("pending", "Pending"),
("enqueued", "Enqueued"),
("progress", "In Progress"),
("finished", "Finished"),
],
default="draft",
default="pending",
required=True,
readonly=True,
tracking=True,
Expand All @@ -52,72 +54,75 @@
"res.company",
readonly=True,
)
is_read = fields.Boolean(default=True)
is_read = fields.Boolean()
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()
def _get_state(self):
self.ensure_one()
job_states = set(self.job_ids.grouped("state").keys())
if all(state in ("done", "cancelled", "failed") for state in job_states):
return "finished"
elif {"done", "started"} & job_states:
return "progress"
elif "enqueued" in job_states:
return "enqueued"
return "pending"

Check warning on line 74 in queue_job_batch/models/queue_job_batch.py

View check run for this annotation

Codecov / codecov/patch

queue_job_batch/models/queue_job_batch.py#L73-L74

Added lines #L73 - L74 were not covered by tests

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
for rec in self:
if (state := rec._get_state()) != rec.state:
rec.state = state

def set_read(self):
res = self.write({"is_read": True})
notifications = []
channel = "queue.job.batch/updated"
notifications.append([self.env.user.partner_id, channel, {}])
self.env["bus.bus"]._sendmany(notifications)
return res
for rec in self:
if rec.is_read or rec.state != "finished":
continue
rec.is_read = True
rec.user_id._bus_send("queue.job.batch/updated", {"batch_read": True})

@api.model
def get_new_batch(self, name, **kwargs):
vals = kwargs.copy()
company_id = self.env.user.company_id.id

if "company_id" in self.env.context:
company_id = self.env.context["company_id"]

vals.update(
{
"user_id": self.env.uid,
"name": name,
"state": "draft",
"company_id": company_id,
"company_id": self.env.company.id or self.env.user.company_id.id,
}
)
return self.sudo().create(vals).with_user(self.env.uid)
record = self.sudo().create(vals).with_user(self.env.uid)
record.user_id._bus_send("queue.job.batch/updated", {"batch_created": True})
return record

@api.depends("job_ids")
@api.depends("job_ids.state")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we really need this depends? Job count is not stored.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really, for the UI. I'd be necessary when reading the field programatically, though, without invalidating caches. It's a habit 😓

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)
for rec in self:
jobs_by_state = rec.job_ids.grouped("state")
rec.job_count = len(rec.job_ids)
rec.failed_job_count = len(jobs_by_state.get("failed", []))
rec.finished_job_count = len(jobs_by_state.get("done", []))
rec.completeness = rec.finished_job_count / max(1, rec.job_count)
rec.failed_percentage = rec.failed_job_count / max(1, rec.job_count)

@api.model
def _to_store_fnames(self):
return (

Check warning on line 114 in queue_job_batch/models/queue_job_batch.py

View check run for this annotation

Codecov / codecov/patch

queue_job_batch/models/queue_job_batch.py#L114

Added line #L114 was not covered by tests
"name",
"state",
"job_count",
"finished_job_count",
"failed_job_count",
"completeness",
"failed_percentage",
)

def _to_store(self, store: Store):
fnames = self._to_store_fnames()

Check warning on line 125 in queue_job_batch/models/queue_job_batch.py

View check run for this annotation

Codecov / codecov/patch

queue_job_batch/models/queue_job_batch.py#L125

Added line #L125 was not covered by tests
for rec in self:
data = rec.read(fnames)[0]
store.add(rec, data)

Check warning on line 128 in queue_job_batch/models/queue_job_batch.py

View check run for this annotation

Codecov / codecov/patch

queue_job_batch/models/queue_job_batch.py#L127-L128

Added lines #L127 - L128 were not covered by tests
30 changes: 30 additions & 0 deletions queue_job_batch/models/res_users.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Copyright 2025 Camptocamp SA (https://www.camptocamp.com).
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import models


class Users(models.Model):
_name = "res.users"
_inherit = ["res.users"]

def _init_store_data(self, store):
res = super()._init_store_data(store)
store.add(

Check warning on line 13 in queue_job_batch/models/res_users.py

View check run for this annotation

Codecov / codecov/patch

queue_job_batch/models/res_users.py#L12-L13

Added lines #L12 - L13 were not covered by tests
{
"hasQueueJobBatchUserGroup": self.env.user.has_group(
"queue_job_batch.group_queue_job_batch_user"
),
}
)
return res

Check warning on line 20 in queue_job_batch/models/res_users.py

View check run for this annotation

Codecov / codecov/patch

queue_job_batch/models/res_users.py#L20

Added line #L20 was not covered by tests

def _get_queue_job_batches(self):
# See :meth:`controllers.webclient.WebClient._process_request_for_internal_user`
self.ensure_one()
return self.env["queue.job.batch"].search(

Check warning on line 25 in queue_job_batch/models/res_users.py

View check run for this annotation

Codecov / codecov/patch

queue_job_batch/models/res_users.py#L24-L25

Added lines #L24 - L25 were not covered by tests
[
("user_id", "=", self.id),
("is_read", "=", False),
]
)
4 changes: 4 additions & 0 deletions queue_job_batch/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@
- [ForgeFlow](https://forgeflow.com):
- Lois Rilo \<<[email protected]>\>
- Jasmin Solanki \<<[email protected]>\>

- [Camptocamp](https://camptocamp.com):
- Maksym Yankin \<<[email protected]>\>
- Iván Todorovich \<<[email protected]>\>
Loading
Loading