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()