From 041a3fb9b2052fdb84875ef997543442c782570b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 15 Jun 2023 17:06:13 +0200 Subject: [PATCH] Support PYSON comparison of timedelta Closes #10727 --- proteus/CHANGELOG | 1 + proteus/proteus/pyson.py | 8 ++++++-- sao/CHANGELOG | 1 + sao/src/pyson.js | 12 +++++++----- sao/tests/sao.js | 18 ++++++++++++++++++ tryton/CHANGELOG | 1 + tryton/tryton/pyson.py | 8 ++++++-- trytond/CHANGELOG | 1 + trytond/trytond/pyson.py | 8 ++++++-- trytond/trytond/tests/test_pyson.py | 15 +++++++++++++++ 10 files changed, 62 insertions(+), 11 deletions(-) diff --git a/proteus/CHANGELOG b/proteus/CHANGELOG index c43a4a2270..eb935d6bab 100644 --- a/proteus/CHANGELOG +++ b/proteus/CHANGELOG @@ -1,3 +1,4 @@ +* Support PYSON comparison of timedelta * Support encoding timedelta into PYSON TimeDelta Version 6.8.0 - 2023-05-01 diff --git a/proteus/proteus/pyson.py b/proteus/proteus/pyson.py index bd189c17d6..353c839022 100644 --- a/proteus/proteus/pyson.py +++ b/proteus/proteus/pyson.py @@ -314,12 +314,14 @@ class Greater(PYSON): if isinstance(i, PYSON): assert i.types().issubset({ int, float, type(None), - datetime.datetime, datetime.date}), \ + datetime.datetime, datetime.date, + datetime.timedelta}), \ 'statement must be an integer, float, date or datetime' else: assert isinstance(i, ( int, float, type(None), - datetime.datetime, datetime.date)), \ + datetime.datetime, datetime.date, + datetime.timedelta)), \ 'statement must be an integer, float, date or datetime' if isinstance(equal, PYSON): if equal.types() != {bool}: @@ -358,6 +360,8 @@ class Greater(PYSON): elif isinstance(stmt, datetime.date): time = datetime.time(0, 0) stmt = datetime.datetime.combine(stmt, time).timestamp() + elif isinstance(stmt, datetime.timedelta): + stmt = stmt.total_seconds() dct[i] = float(stmt) return dct diff --git a/sao/CHANGELOG b/sao/CHANGELOG index a05bfb6939..0eaa7a38d7 100644 --- a/sao/CHANGELOG +++ b/sao/CHANGELOG @@ -1,3 +1,4 @@ +* Support PYSON comparison of timedelta * Support encoding timedelta into PYSON TimeDelta * Eagerly read string value of multiselection fields * Use locale timezone for CSV in locale format diff --git a/sao/src/pyson.js b/sao/src/pyson.js index 9f3aa68fb3..e22ed5a16c 100644 --- a/sao/src/pyson.js +++ b/sao/src/pyson.js @@ -415,15 +415,16 @@ var statement = statements[i]; if (statement instanceof Sao.PYSON.PYSON) { if ( (!(statement instanceof Sao.PYSON.DateTime || - statement instanceof Sao.PYSON.Date)) && + statement instanceof Sao.PYSON.Date || + statement instanceof Sao.PYSON.TimeDelta)) && (jQuery(statement.types()).not(['number']).length) ) { throw 'statement must be an integer, float, ' + - 'date or datetime'; + 'date, datetime or timedelta'; } } else { if (!~['number', 'object'].indexOf(typeof statement)) { throw 'statement must be an integer, float, ' + - 'date or datetime'; + 'date, datetime or timedelta'; } } } @@ -461,8 +462,9 @@ for (var i=0; i < 2; i++) { if (moment.isMoment(values[i])) { values[i] = values[i].valueOf(); - } - else { + } else if (moment.isDuration(values[i])) { + values[i] = values[i].valueOf(); + } else { values[i] = Number(values[i]); } } diff --git a/sao/tests/sao.js b/sao/tests/sao.js index f1ac7ad9bf..406fa6d018 100644 --- a/sao/tests/sao.js +++ b/sao/tests/sao.js @@ -520,6 +520,24 @@ QUnit.strictEqual(new Sao.PYSON.Decoder().decode(eval_), true, 'decode(Greater(PYSON.DateTime(2020, 1, 1, 0, 0, 0, 0), 90000))'); + eval_ = new Sao.PYSON.Encoder().encode(new Sao.PYSON.Greater( + new Sao.PYSON.TimeDelta(5, 2), + new Sao.PYSON.TimeDelta(4, 2))); + QUnit.strictEqual(new Sao.PYSON.Decoder().decode(eval_), true, + 'decode(Greater(PYSON.TimeDelta(5, 2), PYSON.TimeDelta(4, 2)))'); + + eval_ = new Sao.PYSON.Encoder().encode(new Sao.PYSON.Greater( + new Sao.PYSON.TimeDelta(4, 2), + new Sao.PYSON.TimeDelta(5, 2))); + QUnit.strictEqual(new Sao.PYSON.Decoder().decode(eval_), false, + 'decode(Greater(PYSON.TimeDelta(4, 2), PYSON.TimeDelta(5, 2)))'); + + eval_ = new Sao.PYSON.Encoder().encode(new Sao.PYSON.Greater( + new Sao.PYSON.TimeDelta(5, 2), + Sao.TimeDelta(4, 2))); + QUnit.strictEqual(new Sao.PYSON.Decoder().decode(eval_), true, + 'decode(Greater(PYSON.TimeDelta(5, 2), Sao.TimeDelta(4, 2)))'); + eval_ = new Sao.PYSON.Encoder().encode( new Sao.PYSON.Greater(new Sao.PYSON.Eval('i', 0), 0)); QUnit.strictEqual(new Sao.PYSON.Decoder({i: 1}).decode(eval_), true, diff --git a/tryton/CHANGELOG b/tryton/CHANGELOG index f9a3ee3643..32fedc3b8e 100644 --- a/tryton/CHANGELOG +++ b/tryton/CHANGELOG @@ -1,3 +1,4 @@ +* Support PYSON comparison of timedelta * Support encoding timedelta into PYSON TimeDelta * Transfer configuration, profiles and plugins from previous version * Eagerly read string value of multiselection fields diff --git a/tryton/tryton/pyson.py b/tryton/tryton/pyson.py index bd189c17d6..353c839022 100644 --- a/tryton/tryton/pyson.py +++ b/tryton/tryton/pyson.py @@ -314,12 +314,14 @@ class Greater(PYSON): if isinstance(i, PYSON): assert i.types().issubset({ int, float, type(None), - datetime.datetime, datetime.date}), \ + datetime.datetime, datetime.date, + datetime.timedelta}), \ 'statement must be an integer, float, date or datetime' else: assert isinstance(i, ( int, float, type(None), - datetime.datetime, datetime.date)), \ + datetime.datetime, datetime.date, + datetime.timedelta)), \ 'statement must be an integer, float, date or datetime' if isinstance(equal, PYSON): if equal.types() != {bool}: @@ -358,6 +360,8 @@ class Greater(PYSON): elif isinstance(stmt, datetime.date): time = datetime.time(0, 0) stmt = datetime.datetime.combine(stmt, time).timestamp() + elif isinstance(stmt, datetime.timedelta): + stmt = stmt.total_seconds() dct[i] = float(stmt) return dct diff --git a/trytond/CHANGELOG b/trytond/CHANGELOG index b5aa62f339..6497600468 100644 --- a/trytond/CHANGELOG +++ b/trytond/CHANGELOG @@ -1,3 +1,4 @@ +* Support PYSON comparison of timedelta * Support encoding timedelta into PYSON TimeDelta * Allow date formating in sequences substitutions * Make ModelSQL based on table query read only by default diff --git a/trytond/trytond/pyson.py b/trytond/trytond/pyson.py index a4d8f2a781..688492b088 100644 --- a/trytond/trytond/pyson.py +++ b/trytond/trytond/pyson.py @@ -314,12 +314,14 @@ class Greater(PYSON): if isinstance(i, PYSON): assert i.types().issubset({ int, float, type(None), - datetime.datetime, datetime.date}), \ + datetime.datetime, datetime.date, + datetime.timedelta}), \ 'statement must be an integer, float, date or datetime' else: assert isinstance(i, ( int, float, type(None), - datetime.datetime, datetime.date)), \ + datetime.datetime, datetime.date, + datetime.timedelta)), \ 'statement must be an integer, float, date or datetime' if isinstance(equal, PYSON): if equal.types() != {bool}: @@ -358,6 +360,8 @@ class Greater(PYSON): elif isinstance(stmt, datetime.date): time = datetime.time(0, 0) stmt = datetime.datetime.combine(stmt, time).timestamp() + elif isinstance(stmt, datetime.timedelta): + stmt = stmt.total_seconds() dct[i] = float(stmt) return dct diff --git a/trytond/trytond/tests/test_pyson.py b/trytond/trytond/tests/test_pyson.py index e8ea742a4b..eb7d9c759b 100644 --- a/trytond/trytond/tests/test_pyson.py +++ b/trytond/trytond/tests/test_pyson.py @@ -303,6 +303,21 @@ class PYSONTestCase(unittest.TestCase): pyson.DateTime(2020, 1, 1, 0, 0, 0, 0), 90000)) self.assertTrue(pyson.PYSONDecoder().decode(eval)) + eval = pyson.PYSONEncoder().encode(pyson.Greater( + pyson.TimeDelta(5, 2), + pyson.TimeDelta(4, 2))) + self.assertTrue(pyson.PYSONDecoder().decode(eval)) + + eval = pyson.PYSONEncoder().encode(pyson.Greater( + pyson.TimeDelta(4, 2), + pyson.TimeDelta(5, 2))) + self.assertFalse(pyson.PYSONDecoder().decode(eval)) + + eval = pyson.PYSONEncoder().encode(pyson.Greater( + pyson.TimeDelta(5, 2), + datetime.timedelta(4, 2))) + self.assertTrue(pyson.PYSONDecoder().decode(eval)) + eval = pyson.PYSONEncoder().encode( pyson.Greater(pyson.Eval('i', 0), 0)) self.assertTrue(pyson.PYSONDecoder({'i': 1}).decode(eval))