diff --git a/queue_job_batch/models/queue_job.py b/queue_job_batch/models/queue_job.py index ddc3efe879..3c41c9b6a3 100644 --- a/queue_job_batch/models/queue_job.py +++ b/queue_job_batch/models/queue_job.py @@ -25,7 +25,7 @@ def write(self, vals): for record in self: if record.state != "done" and record.job_batch_id: batches |= record.job_batch_id - for batch in batches: + for batch in batches.with_context(job_batch=None): # 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() diff --git a/queue_job_batch/models/queue_job_batch.py b/queue_job_batch/models/queue_job_batch.py index 998f7a5ac2..10812bcbc9 100644 --- a/queue_job_batch/models/queue_job_batch.py +++ b/queue_job_batch/models/queue_job_batch.py @@ -5,6 +5,7 @@ from odoo import api, fields, models from odoo.addons.mail.tools.discuss import Store +from odoo.addons.queue_job.exception import RetryableJobError class QueueJobBatch(models.Model): @@ -58,6 +59,7 @@ class QueueJobBatch(models.Model): completeness = fields.Float( compute="_compute_job_count", ) + execution_time = fields.Float(compute="_compute_job_count") failed_percentage = fields.Float( compute="_compute_job_count", ) @@ -107,8 +109,21 @@ def _compute_job_count(self): 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.execution_time = sum(rec.job_ids.mapped("exec_time")) rec.failed_percentage = rec.failed_job_count / max(1, rec.job_count) + def _on_done(self): + if self.job_count != self.finished_job_count + self.failed_job_count: + raise RetryableJobError( + "%s: %d total jobs != %d finished + %d failed" + % ( + self.name, + self.job_count, + self.finished_job_count, + self.failed_job_count, + ) + ) + @api.model def _to_store_fnames(self): return ( 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..688b7d59f8 --- /dev/null +++ b/queue_job_batch/tests/test_queue_job_batch.py @@ -0,0 +1,50 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.tests import TransactionCase + +from odoo.addons.queue_job.exception import RetryableJobError + + +class TestJobBatch(TransactionCase): + def setUp(self): + super().setUp() + self.job_batch = self.env["queue.job.batch"].create( + { + "name": "test", + "user_id": self.env.user.id, + } + ) + partners = self.env.ref("base.res_partner_1") + self.env.ref( + "base.res_partner_2" + ) + self.jobs = [ + p.with_context(job_batch=self.job_batch).with_delay()._get_complete_name() + for p in partners + ] + self.assertEqual(len(self.job_batch.job_ids), len(self.jobs)) + + def test_execution_time(self): + self.assertEqual(self.job_batch.execution_time, 0) + for job in self.jobs: + job.set_started() + job.perform() + job.set_done() + job.store() + self.assertGreater(job.exec_time, 0) + self.assertEqual( + self.job_batch.execution_time, + self.jobs[0].exec_time + self.jobs[1].exec_time, + ) + + def test_on_done(self): + self.jobs[0].set_started() + self.jobs[0].perform() + self.jobs[0].set_done() + self.jobs[0].store() + with self.assertRaises(RetryableJobError): + self.job_batch._on_done() + self.jobs[1].set_started() + self.jobs[1].perform() + self.jobs[1].set_done() + self.jobs[1].store() + self.job_batch._on_done() diff --git a/queue_job_batch/views/queue_job_batch_views.xml b/queue_job_batch/views/queue_job_batch_views.xml index 34bdc1fee3..b06f383e19 100644 --- a/queue_job_batch/views/queue_job_batch_views.xml +++ b/queue_job_batch/views/queue_job_batch_views.xml @@ -14,6 +14,7 @@ options="{'current_value': 'finished_job_count', 'max_value': 'job_count'}" /> +