changeset 3944a51cb009 in trytond:default
details: https://hg.tryton.org/trytond?cmd=changeset;node=3944a51cb009
description:
        Support timedelta in CSV import/export

        issue9220
        review297531002
diffstat:

 CHANGELOG                             |    1 +
 trytond/model/fields/date.py          |    9 ++-
 trytond/model/modelstorage.py         |   14 ++++
 trytond/tests/export_data.py          |    1 +
 trytond/tests/import_data.py          |    7 ++
 trytond/tests/test_exportdata.py      |   27 +++++++
 trytond/tests/test_field_timedelta.py |  119 +++++++++++++++++++++++++++++----
 trytond/tests/test_importdata.py      |   31 ++++++++
 8 files changed, 193 insertions(+), 16 deletions(-)

diffs (325 lines):

diff -r da06013a9bef -r 3944a51cb009 CHANGELOG
--- a/CHANGELOG Sun Oct 11 17:15:10 2020 +0200
+++ b/CHANGELOG Sun Oct 11 19:04:16 2020 +0200
@@ -1,3 +1,4 @@
+* Support import of timedelta data
 * Add symbol widget
 * Add sql_pairing tool
 * Add format_datetime to Report
diff -r da06013a9bef -r 3944a51cb009 trytond/model/fields/date.py
--- a/trytond/model/fields/date.py      Sun Oct 11 17:15:10 2020 +0200
+++ b/trytond/model/fields/date.py      Sun Oct 11 19:04:16 2020 +0200
@@ -174,7 +174,14 @@
 
     def sql_format(self, value):
         if isinstance(value, (int, float)):
-            raise TypeError("TimeDelta requires a timedelta")
+            value = datetime.timedelta(seconds=value)
+        elif isinstance(value, str):
+            if not value.find(':'):
+                raise ValueError(
+                    "TimeDelta requires a string '%H:%M:%S.%f' or '%H:%M'")
+            hours, minutes, seconds = (value.split(":") + ['00'])[:3]
+            value = datetime.timedelta(
+                hours=int(hours), minutes=int(minutes), seconds=float(seconds))
         return super().sql_format(value)
 
     @classmethod
diff -r da06013a9bef -r 3944a51cb009 trytond/model/modelstorage.py
--- a/trytond/model/modelstorage.py     Sun Oct 11 17:15:10 2020 +0200
+++ b/trytond/model/modelstorage.py     Sun Oct 11 19:04:16 2020 +0200
@@ -891,6 +891,20 @@
                                 value, '%Y-%m-%d %H:%M:%S')
                         else:
                             res = None
+                    elif field_type == 'timedelta':
+                        if isinstance(value, datetime.timedelta):
+                            res = value
+                        elif value:
+                            try:
+                                res = float(value)
+                            except ValueError:
+                                hours, minutes, seconds = (
+                                    value.split(':') + ['00'])[:3]
+                                res = datetime.timedelta(
+                                    hours=int(hours), minutes=int(minutes),
+                                    seconds=float(seconds))
+                        else:
+                            res = None
                     elif field_type == 'many2one':
                         res = get_many2one(this_field_def['relation'], value)
                     elif field_type == 'many2many':
diff -r da06013a9bef -r 3944a51cb009 trytond/tests/export_data.py
--- a/trytond/tests/export_data.py      Sun Oct 11 17:15:10 2020 +0200
+++ b/trytond/tests/export_data.py      Sun Oct 11 19:04:16 2020 +0200
@@ -22,6 +22,7 @@
     text = fields.Text('Text')
     date = fields.Date('Date')
     datetime = fields.DateTime('DateTime')
+    timedelta = fields.TimeDelta('TimeDelta')
     selection = fields.Selection([
             (None, ''),
             ('select1', 'Select 1'),
diff -r da06013a9bef -r 3944a51cb009 trytond/tests/import_data.py
--- a/trytond/tests/import_data.py      Sun Oct 11 17:15:10 2020 +0200
+++ b/trytond/tests/import_data.py      Sun Oct 11 19:04:16 2020 +0200
@@ -71,6 +71,12 @@
     datetime = fields.DateTime('DateTime')
 
 
+class ImportDataTimeDelta(ModelSQL):
+    "Import Data TimeDelta"
+    __name__ = 'test.import_data.timedelta'
+    timedelta = fields.TimeDelta('TimeDelta')
+
+
 class ImportDataSelection(ModelSQL):
     "Import Data Selection"
     __name__ = 'test.import_data.selection'
@@ -163,6 +169,7 @@
         ImportDataText,
         ImportDataDate,
         ImportDataDateTime,
+        ImportDataTimeDelta,
         ImportDataSelection,
         ImportDataMany2OneTarget,
         ImportDataMany2One,
diff -r da06013a9bef -r 3944a51cb009 trytond/tests/test_exportdata.py
--- a/trytond/tests/test_exportdata.py  Sun Oct 11 17:15:10 2020 +0200
+++ b/trytond/tests/test_exportdata.py  Sun Oct 11 19:04:16 2020 +0200
@@ -198,6 +198,33 @@
             [[datetime.datetime(2010, 1, 1, 12, 0, 0)], ['']])
 
     @with_transaction()
+    def test_timedelta(self):
+        'Test export_data timedelta'
+        pool = Pool()
+        ExportData = pool.get('test.export_data')
+
+        export1, = ExportData.create([{
+                    'timedelta': datetime.timedelta(
+                        hours=1, minutes=20, seconds=30.45),
+                    }])
+        self.assertEqual(
+            ExportData.export_data([export1], ['timedelta']),
+            [[datetime.timedelta(hours=1, minutes=20, seconds=30.45)]])
+
+        export2, = ExportData.create([{
+                    'timedelta': None,
+                    }])
+        self.assertEqual(
+            ExportData.export_data([export2], ['timedelta']),
+            [['']])
+
+        self.assertEqual(
+            ExportData.export_data([export1, export2], ['timedelta']), [
+                [datetime.timedelta(hours=1, minutes=20, seconds=30.45)],
+                [''],
+                ])
+
+    @with_transaction()
     def test_selection(self):
         'Test export_data selection'
         pool = Pool()
diff -r da06013a9bef -r 3944a51cb009 trytond/tests/test_field_timedelta.py
--- a/trytond/tests/test_field_timedelta.py     Sun Oct 11 17:15:10 2020 +0200
+++ b/trytond/tests/test_field_timedelta.py     Sun Oct 11 19:04:16 2020 +0200
@@ -53,11 +53,37 @@
         self.assertEqual(timedelta.timedelta, default_timedelta)
 
     @with_transaction()
-    def test_create_non_timedelta(self):
-        "Test create timedelta with non timedelta"
+    def test_create_string(self):
+        "Test create timedelta with timedelta string"
         TimeDelta = Pool().get('test.timedelta')
 
-        with self.assertRaises(TypeError):
+        timedelta, = TimeDelta.create([{
+                    'timedelta': '1:20:30.5',
+                    }])
+
+        self.assertEqual(
+            timedelta.timedelta,
+            datetime.timedelta(hours=1, minutes=20, seconds=30.5))
+
+    @with_transaction()
+    def test_create_string_simplet(self):
+        "Test create timedelta with simple timedelta string"
+        TimeDelta = Pool().get('test.timedelta')
+
+        timedelta, = TimeDelta.create([{
+                    'timedelta': '1:20',
+                    }])
+
+        self.assertEqual(
+            timedelta.timedelta,
+            datetime.timedelta(hours=1, minutes=20))
+
+    @with_transaction()
+    def test_create_invalid_string(self):
+        "Test create timedelta with invalid timedelta string"
+        TimeDelta = Pool().get('test.timedelta')
+
+        with self.assertRaises(ValueError):
             TimeDelta.create([{
                         'timedelta': 'non timedelta',
                         }])
@@ -67,10 +93,22 @@
         "Test create timedelta with integer"
         TimeDelta = Pool().get('test.timedelta')
 
-        with self.assertRaises(TypeError):
-            TimeDelta.create([{
-                        'timedelta': 42,
-                        }])
+        timedelta, = TimeDelta.create([{
+                    'timedelta': 42,
+                    }])
+
+        self.assertEqual(timedelta.timedelta, datetime.timedelta(seconds=42))
+
+    @with_transaction()
+    def test_create_float(self):
+        "Test create timedelta with float"
+        TimeDelta = Pool().get('test.timedelta')
+
+        timedelta, = TimeDelta.create([{
+                    'timedelta': 42.5,
+                    }])
+
+        self.assertEqual(timedelta.timedelta, datetime.timedelta(seconds=42.5))
 
     @with_transaction()
     def test_create_required_with_value(self):
@@ -362,30 +400,81 @@
         self.assertEqual(timedelta.timedelta, minute)
 
     @with_transaction()
-    def test_write_non_timedelta(self):
-        "Test write timedelta with non timedelta"
+    def test_write_string(self):
+        "Test write timedelta with timedelta string"
+        TimeDelta = Pool().get('test.timedelta')
+        timedelta, = TimeDelta.create([{
+                    'timedelta': hour,
+                    }])
+
+        TimeDelta.write([timedelta], {
+                'timedelta': '1:20:30.5',
+                })
+
+        self.assertEqual(
+            timedelta.timedelta,
+            datetime.timedelta(hours=1, minutes=20, seconds=30.5))
+
+    @with_transaction()
+    def test_write_string_simple(self):
+        "Test write timedelta with simple timedelta string"
         TimeDelta = Pool().get('test.timedelta')
         timedelta, = TimeDelta.create([{
                     'timedelta': hour,
                     }])
 
-        with self.assertRaises(TypeError):
+        TimeDelta.write([timedelta], {
+                'timedelta': '1:20',
+                })
+
+        self.assertEqual(
+            timedelta.timedelta,
+            datetime.timedelta(hours=1, minutes=20))
+
+    @with_transaction()
+    def test_write_invalid_string(self):
+        "Test write timedelta with invalid timedelta string"
+        TimeDelta = Pool().get('test.timedelta')
+        timedelta, = TimeDelta.create([{
+                    'timedelta': hour,
+                    }])
+
+        with self.assertRaises(ValueError):
             TimeDelta.write([timedelta], {
                     'timedelta': 'non timedelta',
                     })
 
     @with_transaction()
-    def test_write_integer(self):
+    def test_write_interger(self):
         "Test write timedelta with integer"
         TimeDelta = Pool().get('test.timedelta')
         timedelta, = TimeDelta.create([{
                     'timedelta': hour,
                     }])
 
-        with self.assertRaises(TypeError):
-            TimeDelta.write([timedelta], {
-                    'timedelta': 42,
-                    })
+        TimeDelta.write([timedelta], {
+                'timedelta': 42,
+                })
+
+        self.assertEqual(
+            timedelta.timedelta,
+            datetime.timedelta(seconds=42))
+
+    @with_transaction()
+    def test_write_float(self):
+        "Test write timedelta with float"
+        TimeDelta = Pool().get('test.timedelta')
+        timedelta, = TimeDelta.create([{
+                    'timedelta': hour,
+                    }])
+
+        TimeDelta.write([timedelta], {
+                'timedelta': 42.5,
+                })
+
+        self.assertEqual(
+            timedelta.timedelta,
+            datetime.timedelta(seconds=42.5))
 
 
 def suite():
diff -r da06013a9bef -r 3944a51cb009 trytond/tests/test_importdata.py
--- a/trytond/tests/test_importdata.py  Sun Oct 11 17:15:10 2020 +0200
+++ b/trytond/tests/test_importdata.py  Sun Oct 11 19:04:16 2020 +0200
@@ -353,6 +353,37 @@
             ['datetime'], [['foo']])
 
     @with_transaction()
+    def test_timedelta(self):
+        'Test timedelta'
+        pool = Pool()
+        Timedelta = pool.get('test.import_data.timedelta')
+
+        self.assertEqual(Timedelta.import_data(['timedelta'],
+            [['00:00']]), 1)
+
+        self.assertEqual(Timedelta.import_data(['timedelta'],
+            [['0:00:00']]), 1)
+
+        self.assertEqual(Timedelta.import_data(['timedelta'],
+            [['01:00:00']]), 1)
+
+        self.assertEqual(Timedelta.import_data(['timedelta'],
+            [['36:00:00']]), 1)
+
+        self.assertEqual(Timedelta.import_data(['timedelta'],
+            [['0:00:00.0001']]), 1)
+
+        self.assertEqual(Timedelta.import_data(['timedelta'],
+            [[datetime.timedelta(
+                weeks=2, days=3, hours=8, minutes=50, seconds=30.45)]]), 1)
+
+        self.assertEqual(Timedelta.import_data(['timedelta'],
+            [[30.45]]), 1)
+
+        self.assertRaises(ValueError, Timedelta.import_data,
+            ['timedelta'], [['foo']])
+
+    @with_transaction()
     def test_selection(self):
         'Test selection'
         pool = Pool()

Reply via email to