diff --git a/queue_job/__manifest__.py b/queue_job/__manifest__.py
index 5ef7214eab..1b873fc086 100644
--- a/queue_job/__manifest__.py
+++ b/queue_job/__manifest__.py
@@ -3,7 +3,7 @@
{
"name": "Job Queue",
- "version": "13.0.3.6.0",
+ "version": "13.0.3.7.0",
"author": "Camptocamp,ACSONE SA/NV,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/queue/queue_job",
"license": "LGPL-3",
diff --git a/queue_job/job.py b/queue_job/job.py
index 45dca7cbb6..7edcb5c1ef 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:
@@ -661,6 +664,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 1395499c0b..2b2b838961 100644
--- a/queue_job/models/queue_job.py
+++ b/queue_job/models/queue_job.py
@@ -79,6 +79,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 2ef6eab120..03db708400 100644
--- a/queue_job/views/queue_job_views.xml
+++ b/queue_job/views/queue_job_views.xml
@@ -56,6 +56,8 @@
+
+
@@ -107,12 +109,34 @@
+
+
+ queue.job.pivot
+ queue.job
+
+
+
+
+
+
+
+
+
+ queue.job.graph
+ queue.job
+
+
+
+
+
+
+
queue.job.search
queue.job
@@ -178,7 +202,7 @@
Jobs
queue.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 0b8245618d..308b52501e 100644
--- a/test_queue_job/tests/test_job.py
+++ b/test_queue_job/tests/test_job.py
@@ -149,6 +149,7 @@ 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)
@@ -157,6 +158,7 @@ def test_set_done(self):
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):
@@ -233,6 +235,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": "!"})