From 96b7509b63958523ef7daeefa3b86709d45c478f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 8 May 2010 17:35:44 +0200 Subject: [PATCH 001/140] Increase version number --- trytond/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/version.py b/trytond/version.py index 36e59dcc47..524015e4b9 100644 --- a/trytond/version.py +++ b/trytond/version.py @@ -1,7 +1,7 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. PACKAGE = "trytond" -VERSION = "1.6.0" +VERSION = "1.6.1" LICENSE = "GPL-3" WEBSITE = "http://www.tryton.org/" From 8d0a1398429eed17424d32f876bfa9999f2c5180 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 8 May 2010 18:10:14 +0200 Subject: [PATCH 002/140] Include xml file from test (transplanted from 22ecbfe18248ba407f874ee64b0d25db6be55df8) --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index cd33f265dc..a0c7156328 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -18,6 +18,7 @@ include trytond/ir/ui/*.rng include trytond/ir/ui/*.rnc include trytond/res/*.xml include trytond/res/*.csv +include trytond/test/*.xml include trytond/webdav/*.xml include trytond/webdav/*.csv include trytond/workflow/*.xml From 07e1fd31dd7dd20dcb505c458735e7b611debd97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 9 May 2010 01:09:36 +0200 Subject: [PATCH 003/140] Try to find module in sys.path before using dirname of ep.dist.location (transplanted from 721eafa7463056c92c58410187255621da7174e9) --- trytond/modules/__init__.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/trytond/modules/__init__.py b/trytond/modules/__init__.py index 62dbc348f3..f1b27a9dae 100644 --- a/trytond/modules/__init__.py +++ b/trytond/modules/__init__.py @@ -382,9 +382,16 @@ def register_classes(reload_p=False): mod_path = os.path.join(ep.dist.location, *ep.module_name.split('.')[:-1]) if not os.path.isdir(mod_path): - # When testing modules from setuptools location is the module - # directory - mod_path = os.path.dirname(ep.dist.location) + # Find module in path + for path in sys.path: + mod_path = os.path.join(path, + *ep.module_name.split('.')[:-1]) + if os.path.isdir(os.path.join(mod_path, module)): + break + if not os.path.isdir(os.path.join(mod_path, module)): + # When testing modules from setuptools location is the + # module directory + mod_path = os.path.dirname(ep.dist.location) mod_file, pathname, description = imp.find_module(module, [mod_path]) try: From c4c1838037e5d2945bbbb8853f2424344def28d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 9 May 2010 11:04:18 +0200 Subject: [PATCH 004/140] Try to find file in sys.path before using dirname of ep.dist.location (transplanted from 5dc4d90783ab506db3d2b2ffe707675bc8018af1) --- trytond/tools/misc.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/trytond/tools/misc.py b/trytond/tools/misc.py index dfaee902d4..8967189453 100644 --- a/trytond/tools/misc.py +++ b/trytond/tools/misc.py @@ -4,7 +4,7 @@ """ Miscelleanous tools used by tryton """ -import os, time +import os, time, sys import inspect from trytond.config import CONFIG import subprocess @@ -89,10 +89,18 @@ def file_open(name, mode="r", subdir='modules'): *epoint.module_name.split('.')[:-1]) name3 = os.path.join(mod_path, name) if not os.path.isfile(name3): - # When testing modules from setuptools location is the module - # directory - name3 = os.path.join(os.path.dirname(epoint.dist.location), - name) + # Find module in path + for path in sys.path: + mod_path = os.path.join(path, + *epoint.module_name.split('.')[:-1]) + name3 = os.path.join(mod_path, name) + if os.path.isfile(name3): + break + if not os.path.isfile(name3): + # When testing modules from setuptools location is the + # module directory + name3 = os.path.join(os.path.dirname(epoint.dist.location), + name) if subdir: if subdir == 'modules'\ From b15934a9df76154e34928ac609071da37269a95d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 28 May 2010 20:29:16 +0200 Subject: [PATCH 005/140] Fix wrong password for database creation in tests (transplanted from 0f91cef41a4df4b897b178d84b3a1e85943fed20) --- trytond/tests/test_tryton.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/tests/test_tryton.py b/trytond/tests/test_tryton.py index aceb03b321..c913305ff8 100644 --- a/trytond/tests/test_tryton.py +++ b/trytond/tests/test_tryton.py @@ -92,7 +92,7 @@ def install_module(name): databases = database.list(cursor) cursor.close() if DB_NAME not in databases: - create(DB_NAME, USER_PASSWORD, 'en_US', CONFIG['admin_passwd']) + create(DB_NAME, CONFIG['admin_passwd'], 'en_US', USER_PASSWORD) cursor = DB.cursor() module_obj = POOL.get('ir.module.module') module_ids = module_obj.search(cursor, USER, [ From 7e8854a17c9c6d68ab79eef9b3bfe6ef2d44416f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Tue, 1 Jun 2010 20:21:01 +0200 Subject: [PATCH 006/140] Convert str id into int (transplanted from 71c2427dd0ef0d9a61c9277479ea68b22dc98255) --- trytond/webdav/webdav.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/webdav/webdav.py b/trytond/webdav/webdav.py index cb72bc08cd..24c3350904 100644 --- a/trytond/webdav/webdav.py +++ b/trytond/webdav/webdav.py @@ -620,7 +620,7 @@ class Attachment(ModelSQL, ModelView): if attachment.resource: model_name, record_id = attachment.resource.split(',') if model_name == 'webdav.collection': - collection = collection_obj.browse(cursor, user, record_id) + collection = collection_obj.browse(cursor, user, int(record_id)) for child in collection.childs: if child.name == attachment.name: return False From 73ae55ee7edfeb5a6e239207b5266516ef6a3486 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 3 Jun 2010 19:47:13 +0200 Subject: [PATCH 007/140] get_eval must return a list of id for O2M for issue1572 (transplanted from 221282dc8954c9eece25181e55053b2057809c87) --- trytond/model/browse.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/trytond/model/browse.py b/trytond/model/browse.py index 5c516c0c54..4880d602e3 100644 --- a/trytond/model/browse.py +++ b/trytond/model/browse.py @@ -13,14 +13,7 @@ class BrowseRecordList(list): self.context = context def get_eval(self): - res = [] - for record in self: - res2 = {} - for field_name, _ in record._model._columns.iteritems(): - if not isinstance(record[field_name], BrowseRecordList): - res2[field_name] = record.get_eval(field_name) - res.append(res2) - return res + return [record.get_eval() for record in self] class BrowseRecordNull(object): From 29204b08510e84bd661e8f3a5c6a1b3727e5a352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 6 Jun 2010 11:30:33 +0200 Subject: [PATCH 008/140] Extend Float to work with Decimal for issue1575 thanks to gadaga (transplanted from 15c673a1072da72939845e792ec9869aba9773a7) --- trytond/backend/fields.py | 2 +- trytond/protocols/datatype.py | 138 ++++++++- trytond/tests/test_protocols_datatype.py | 368 +++++++++++++++++++++++ trytond/tests/test_tryton.py | 2 + 4 files changed, 505 insertions(+), 5 deletions(-) create mode 100644 trytond/tests/test_protocols_datatype.py diff --git a/trytond/backend/fields.py b/trytond/backend/fields.py index 4b5735a5a0..6fa95c10cf 100644 --- a/trytond/backend/fields.py +++ b/trytond/backend/fields.py @@ -82,7 +82,7 @@ class Numeric(Float): if isinstance(value, (int, long)): value = Decimal(str(value)) if isinstance(value, float) and hasattr(value, 'decimal'): - value = Decimal(value.decimal) + value = value.decimal assert isinstance(value, Decimal) return value diff --git a/trytond/protocols/datatype.py b/trytond/protocols/datatype.py index de49e973fc..792d6e5141 100644 --- a/trytond/protocols/datatype.py +++ b/trytond/protocols/datatype.py @@ -1,18 +1,148 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. +from decimal import Decimal class Float(float): - def __init__(self, value): super(Float, self).__init__() - self.__decimal = value + self.__decimal = Decimal(value) @property def decimal(self): return self.__decimal def __repr__(self): - return self.decimal + return str(self.decimal) def __str__(self): - return self.decimal + return str(self.decimal) + + def __abs__(self, round=True, context=None): + return Float(str(abs(self.decimal))) + + def __add__(self, other, context=None): + if isinstance(other, Decimal): + return self.decimal.__add__(other, context=context) + return super(Float, self).__add__(other) + + def __radd__(self, other, context=None): + if isinstance(other, Decimal): + return self.decimal.__radd__(other, context=context) + return super(Float, self).__radd__(other) + + def __div__(self, other, context=None): + if isinstance(other, Decimal): + return self.decimal.__div__(other, context=context) + return super(Float, self).__div__(other) + + def __rdiv__(self, other, context=None): + if isinstance(other, Decimal): + return self.decimal.__rdiv__(other, context=context) + return super(Float, self).__rdiv__(other) + + def __divmod__(self, other, context=None): + if isinstance(other, Decimal): + return self.decimal.__divmod__(other, context=context) + return super(Float, self).__divmod__(other) + + def __rdivmod__(self, other, context=None): + if isinstance(other, Decimal): + return self.decimal.__rdivmod__(other, context=context) + return super(Float, self).__rdivmod__(other) + + def __eq__(self, other): + if isinstance(other, Decimal): + return self.decimal.__eq__(other) + return super(Float, self).__eq__(other) + + def __floordiv__(self, other, context=None): + if isinstance(other, Decimal): + return self.decimal.__floordiv__(other, context=context) + return super(Float, self).__floordiv__(other) + + def __rfloordiv__(self, other, context=None): + if isinstance(other, Decimal): + return self.decimal.__rfloordiv__(other, context=context) + return super(Float, self).__rfloordiv__(other) + + def __format__(self, specifier, context=None): + return self.decimal.__format__(specifier, context=context) + + def __ge__(self, other, context=None): + if isinstance(other, Decimal): + return self.decimal.__ge__(other, context=context) + return super(Float, self).__ge__(other) + + def __gt__(self, other, context=None): + if isinstance(other, Decimal): + return self.decimal.__gt__(other, context=context) + return super(Float, self).__gt__(other) + + def __le__(self, other, context=None): + if isinstance(other, Decimal): + return self.decimal.__le__(other, context=context) + return super(Float, self).__le__(other) + + def __lt__(self, other, context=None): + if isinstance(other, Decimal): + return self.decimal.__lt__(other, context=context) + return super(Float, self).__lt__(other) + + def __mod__(self, other, context=None): + if isinstance(other, Decimal): + return self.decimal.__mod__(other, context=context) + return super(Float, self).__mod__(other) + + def __rmod__(self, other, context=None): + if isinstance(other, Decimal): + return self.decimal.__rmod__(other, context=context) + return super(Float, self).__rmod__(other) + + def __mul__(self, other, context=None): + if isinstance(other, Decimal): + return self.decimal.__mul__(other, context=context) + return super(Float, self).__mul__(other) + + def __rmul__(self, other, context=None): + if isinstance(other, Decimal): + return self.decimal.__rmul__(other, context=context) + return super(Float, self).__rmul__(other) + + def __ne__(self, other): + if isinstance(other, Decimal): + return self.decimal.__ne__(other) + return super(Float, self).__ne__(other) + + def __neg__(self, context=None): + return Float(str(-self.decimal)) + + def __nonzero__(self): + return self.decimal.__nonzero__() + + def __pos__(self): + return Float(str(self.decimal)) + + def __pow__(self, other, modulo=None, context=None): + if isinstance(other, Decimal): + return self.decimal.__pow__(other, modulo, context) + return super(Float, self).__pow__(other, modulo) + + def __rpow__(self, other, context=None): + if isinstance(other, Decimal): + return self.decimal.__rpow__(other, context=context) + return super(Float, self).__rpow__(other) + + def __sub__(self, other, context=None): + if isinstance(other, Decimal): + return self.decimal.__sub__(other, context=context) + return super(Float, self).__sub__(other) + + def __rsub__(self, other, context=None): + if isinstance(other, Decimal): + return self.decimal.__rsub__(other, context=context) + return super(Float, self).__rsub__(other) + + def __truediv__(self, other, context=None): + if isinstance(other, Decimal): + return self.decimal.__truediv__(other, context=context) + return super(Float, self).__truediv__(other) diff --git a/trytond/tests/test_protocols_datatype.py b/trytond/tests/test_protocols_datatype.py new file mode 100644 index 0000000000..0bd1bbec14 --- /dev/null +++ b/trytond/tests/test_protocols_datatype.py @@ -0,0 +1,368 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +#This file is part of Tryton. The COPYRIGHT file at the top level of +#this repository contains the full copyright notices and license terms. + +import sys, os +DIR = os.path.abspath(os.path.normpath(os.path.join(__file__, + '..', '..', '..', 'trytond'))) +if os.path.isdir(DIR): + sys.path.insert(0, os.path.dirname(DIR)) + +import unittest +from decimal import Decimal +from trytond.protocols.datatype import Float + +class ProtocolsDatatypeFloatTestCase(unittest.TestCase): + ''' + Test Float. + ''' + def test_abs(self): + ''' + Test absolute Float + ''' + self.assertEqual(abs(Float('-1.0')), Float('1.0')) + self.assert_(isinstance(abs(Float('-1.0')), Float)) + + def test_Float_add_Decimal(self): + ''' + Test addition between Float and Decimal + ''' + self.assertEqual(Float('1.0') + Decimal('1.0'), Decimal('2.0')) + + def test_Float_add_float(self): + ''' + Test addition between Float and float + ''' + self.assertEqual(Float('1.0') + 1.0, 2.0) + + def test_Decimal_radd__Float(self): + ''' + Test right addition between Float and Decimal + ''' + self.assertEqual(Decimal('1.0') + Float('1.0'), Decimal('2.0')) + + def test_float_radd__Float(self): + ''' + Test right addition between Float and float + ''' + self.assertEqual(1.0 + Float('1.0'), 2.0) + + def test_Float_div_Decimal(self): + ''' + Test division between Float and Decimal + ''' + self.assertEqual(Float('1.0') / Decimal('1.0'), Decimal('1.0')) + + def test_Float_div_float(self): + ''' + Test division between Float and float + ''' + self.assertEqual(Float('1.0') / 1.0, 1.0) + + def test_Decimal_rdiv_Float(self): + ''' + Test right division between Float and Decimal + ''' + self.assertEqual(Decimal('1.0') / Float('1.0'), Decimal('1.0')) + + def test_float_rdiv_Float(self): + ''' + Test right division between Float and float + ''' + self.assertEqual(1.0 / Float('1.0'), 1.0) + + def test_Float_divmod_Decimal(self): + ''' + Test divmod between Float and Decimal + ''' + self.assertEqual(divmod(Float('2.1'), Decimal('20.5')), + divmod(Decimal('2.1'), Decimal('20.5'))) + + def test_Float_divmod_float(self): + ''' + Test divmod between Float and float + ''' + self.assertEqual(divmod(Float('2.1'), 20.5), divmod(2.1, 20.5)) + + def test_Decimal_rdivmod_float(self): + ''' + Test right divmod between Float and Decimal + ''' + self.assertEqual(divmod(Decimal('2.1'), Float('20.5')), + divmod(Decimal('2.1'), Decimal('20.5'))) + + def test_float_rdivmod_Float(self): + ''' + Test right divmod between Float and float + ''' + self.assertEqual(divmod(2.1, Float('20.5')), divmod(2.1, 20.5)) + + def test_Float_eq_Decimal(self): + ''' + Test equal between Float and Decimal + ''' + self.assert_(Float('1.1') == Decimal('1.1')) + self.assertFalse(Float('1.1') == Decimal('1.0')) + + def test_Float_eq_float(self): + ''' + Test equal between Float and float + ''' + self.assert_(Float('1.1') == 1.1) + self.assertFalse(Float('1.1') == 1.0) + + def test_Decimal_eq_Float(self): + ''' + Test equal between Decimal and Float + ''' + self.assert_(Decimal('1.1') == Float('1.1')) + self.assertFalse(Decimal('1.1') == Float('1.0')) + + def test_float_eq_Float(self): + ''' + Test equal between float and Decimal + ''' + self.assert_(1.1 == Float('1.1')) + self.assertFalse(1.1 == Float('1.0')) + + def test_Float_floordiv_Decimal(self): + ''' + Test floordiv between Float and Decimal + ''' + self.assertEqual(Float('2.5') // Decimal('2.1'), Decimal('1')) + + def test_Float_floordiv_float(self): + ''' + Test floordiv between Float and float + ''' + self.assertEqual(Float('2.5') // 2.1, 1.0) + + def test_Decimal_rfloordiv_Float(self): + ''' + Test rfloordiv between Decimal and Float + ''' + self.assertEqual(Decimal('2.5') // Float('2.1'), Decimal('1')) + + def test_float_rfloordiv_Float(self): + ''' + Test rfloordiv between float and Float + ''' + self.assertEqual(2.5 // Float('2.1'), 1.0) + + def test_Float_format(self): + ''' + Test format Float + ''' + self.assertEqual(format(Float('1.1'), '.32f'), + '1.10000000000000000000000000000000') + + def test_Float_ge_Decimal(self): + ''' + Test ge between Float and Decimal + ''' + self.assert_(Float('1.5') >= Decimal('1.1')) + self.assertFalse(Float('1.1') >= Decimal('1.5')) + + def test_Float_ge_float(self): + ''' + Test ge between Float and float + ''' + self.assert_(Float('1.5') >= 1.1) + self.assertFalse(Float('1.1') >= 1.5) + + def test_Float_gt_Decimal(self): + ''' + Test gt between Float and Decimal + ''' + self.assert_(Float('1.5') > Decimal('1.1')) + self.assertFalse(Float('1.1') > Decimal('1.5')) + + def test_Float_gt_float(self): + ''' + Test gt between Float and float + ''' + self.assert_(Float('1.5') > 1.1) + self.assertFalse(Float('1.1') > 1.5) + + def test_Float_le_Decimal(self): + ''' + Test le between Float and Decimal + ''' + self.assert_(Float('1.1') <= Decimal('1.5')) + self.assertFalse(Float('1.5') <= Decimal('1.1')) + + def test_Float_le_float(self): + ''' + Test le between Float and float + ''' + self.assert_(Float('1.1') <= 1.5) + self.assertFalse(Float('1.5') <= 1.1) + + def test_Float_lt_Decimal(self): + ''' + Test lt between Float and Decimal + ''' + self.assert_(Float('1.1') < Decimal('1.5')) + self.assertFalse(Float('1.5') < Decimal('1.1')) + + def test_Float_lt_float(self): + ''' + Test lt between Float and float + ''' + self.assert_(Float('1.1') < 1.5) + self.assertFalse(Float('1.5') < 1.1) + + def test_Float_mod_Decimal(self): + ''' + Test modulo between Float and Decimal + ''' + self.assertEqual(Float('2.1') % Decimal('20.5'), + Decimal('2.1') % Decimal('20.5')) + + def test_Float_mod_float(self): + ''' + Test modulo between Float and float + ''' + self.assertEqual(Float('2.1') % 20.5, 2.1 % 20.5) + + def test_Decimal_rmod_Float(self): + ''' + Test right modulo between Decimal and Float + ''' + self.assertEqual(Decimal('2.1') % Float('20.5'), + Decimal('2.1') % Decimal('20.5')) + + def test_float_rmod_Float(self): + ''' + Test right modulo between float and Float + ''' + self.assertEqual(2.1 % Float('20.5'), 2.1 % 20.5) + + def test_Float_mul_Decimal(self): + ''' + Test multiplication between Float and Decimal + ''' + self.assertEqual(Float('1.5') * Decimal('1.2'), Decimal('1.8')) + + def test_Float_mul_float(self): + ''' + Test multiplication between Float and float + ''' + self.assertEqual(Float('1.5') * 1.2, 1.5 * 1.2) + + def test_Decimal_rmul_Float(self): + ''' + Test right multiplication between Decimal and Float + ''' + self.assertEqual(Decimal('1.5') * Float('1.2'), Decimal('1.8')) + + def test_float_rmul_Float(self): + ''' + Test right multiplication between float and Float + ''' + self.assertEqual(1.5 * Float('1.2'), 1.5 * 1.2) + + def test_Float_ne_Decimal(self): + ''' + Test not equal between Float and Decimal + ''' + self.assert_(Float('1.1') != Decimal('1.2')) + self.assertFalse(Float('1.1') != Decimal('1.1')) + + def test_Float_ne_float(self): + ''' + Test not equal between Float and float + ''' + self.assert_(Float('1.1') != 1.2) + self.assertFalse(Float('1.1') != 1.1) + + def test_Decimal_ne_Float(self): + ''' + Test not equal between Decimal and Float + ''' + self.assert_(Decimal('1.1') != Float('1.2')) + self.assertFalse(Decimal('1.1') != Float('1.1')) + + def test_float_ne_Float(self): + ''' + Test not equal between float and Decimal + ''' + self.assert_(1.1 != Float('1.2')) + self.assertFalse(1.1 != Float('1.1')) + + def test_neg(self): + ''' + Test negative Float + ''' + self.assertEqual(-Float('1.0'), Float('-1.0')) + self.assert_(isinstance(-Float('1.0'), Float)) + + def test_nonzero(self): + ''' + Test non zero Float + ''' + self.assert_(Float('1.0') != 0) + self.assertFalse(Float('0.0') != 0) + + def test_pos(self): + ''' + Test positive Float + ''' + self.assertEqual(+Float('1.0'), Float('1.0')) + self.assert_(isinstance(+Float('1.0'), Float)) + + def test_Float_pow_Decimal(self): + ''' + Test pow between Float and Decimal + ''' + self.assertEqual(Float('2.5') ** Decimal('2.0'), Decimal('6.25')) + + def test_Float_pow_float(self): + ''' + Test pow between Float and float + ''' + self.assertEqual(Float('2.5') ** 2.0, 6.25) + + def test_Decimal_rpow_float(self): + ''' + Test right pow between Decimal and Float + ''' + self.assertEqual(Decimal('2.5') ** Float('2.0'), Decimal('6.25')) + + def test_float_rpow_Float(self): + ''' + Test right pow between Float and float + ''' + self.assert_(2.5 ** Float('2.0'), 6.25) + + def test_Float_sub_Decimal(self): + ''' + Test substration between Float and Decimal + ''' + self.assertEqual(Float('2.5') - Decimal('1.2'), Decimal('1.3')) + + def test_Float_sub_float(self): + ''' + Test substraction between Float and float + ''' + self.assertEqual(Float('2.5') - 1.2, 1.3) + + def test_Decimal_rsub_Float(self): + ''' + Test right substraction between Decimal and Float + ''' + self.assertEqual(Decimal('2.5') - Float('1.2'), Decimal('1.3')) + + def test_float_rsub_Float(self): + ''' + Test right substraction between float and Float + ''' + self.assertEqual(2.5 - Float('1.2'), 1.3) + +def suite(): + return unittest.TestLoader().loadTestsFromTestCase(ProtocolsDatatypeFloatTestCase) + +if __name__ == '__main__': + suite = suite() + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/trytond/tests/test_tryton.py b/trytond/tests/test_tryton.py index c913305ff8..c5c57b0d0b 100644 --- a/trytond/tests/test_tryton.py +++ b/trytond/tests/test_tryton.py @@ -172,6 +172,8 @@ def all_suite(): suite_.addTests(test_importdata.suite()) import trytond.tests.test_exportdata as test_exportdata suite_.addTests(test_exportdata.suite()) + import trytond.tests.test_protocols_datatype as test_protocols_datatype + suite_.addTests(test_protocols_datatype.suite()) return suite_ def modules_suite(): From 6e9fdd8c4fb2dc4d0b935b497c46112bd33de657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 6 Jun 2010 11:39:30 +0200 Subject: [PATCH 009/140] Allow to use EXTRACT EPOCH with MySQL (transplanted from fc31fe2b207fe294ce00e33430798f0616c48f3e) --- trytond/backend/mysql/database.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/trytond/backend/mysql/database.py b/trytond/backend/mysql/database.py index dae9957c10..60ad2bdc17 100644 --- a/trytond/backend/mysql/database.py +++ b/trytond/backend/mysql/database.py @@ -15,6 +15,8 @@ import time import tempfile QUOTE_SEPARATION = re.compile(r"(.*?)('.*?')", re.DOTALL) +EXTRACT_EPOCH_PATTERN = re.compile(r'EXTRACT\s*\(\s*EPOCH\s+FROM', + re.I) DatabaseIntegrityError = None DatabaseOperationalError = None @@ -233,6 +235,8 @@ class Cursor(CursorInterface): buf = "" for nquote, quote in QUOTE_SEPARATION.findall(sql+"''"): nquote = nquote.replace('ilike', 'like') + nquote = re.sub(EXTRACT_EPOCH_PATTERN, r'UNIX_TIMESTAMP(', + nquote) buf += nquote + quote sql = buf[:-2] try: From 1b5224feb43cc337b1d25897c878919878419a06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 7 Jun 2010 21:27:49 +0200 Subject: [PATCH 010/140] Fix Float for Python < 2.6 and handle extra methods on Decimal (transplanted from 945974a3965a75eb7301405e484470f6f2d74e5f) --- trytond/protocols/datatype.py | 23 +++++++++++++++++++---- trytond/tests/test_protocols_datatype.py | 18 ++++++++++++++++-- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/trytond/protocols/datatype.py b/trytond/protocols/datatype.py index 792d6e5141..ce2e9a84c9 100644 --- a/trytond/protocols/datatype.py +++ b/trytond/protocols/datatype.py @@ -70,22 +70,34 @@ class Float(float): def __ge__(self, other, context=None): if isinstance(other, Decimal): - return self.decimal.__ge__(other, context=context) + if hasattr(self.decimal, '__ge__'): + return self.decimal.__ge__(other, context=context) + # For Python < 2.6 + return self.decimal >= other return super(Float, self).__ge__(other) def __gt__(self, other, context=None): if isinstance(other, Decimal): - return self.decimal.__gt__(other, context=context) + if hasattr(self.decimal, '__gt__'): + return self.decimal.__gt__(other, context=context) + # For Python < 2.6 + return self.decimal > other return super(Float, self).__gt__(other) def __le__(self, other, context=None): if isinstance(other, Decimal): - return self.decimal.__le__(other, context=context) + if hasattr(self.decimal, '__le__'): + return self.decimal.__le__(other, context=context) + # For Python < 2.6 + return self.decimal <= other return super(Float, self).__le__(other) def __lt__(self, other, context=None): if isinstance(other, Decimal): - return self.decimal.__lt__(other, context=context) + if hasattr(self.decimal, '__lt__'): + return self.decimal.__lt__(other, context=context) + # For Python < 2.6 + return self.decimal < other return super(Float, self).__lt__(other) def __mod__(self, other, context=None): @@ -146,3 +158,6 @@ class Float(float): if isinstance(other, Decimal): return self.decimal.__truediv__(other, context=context) return super(Float, self).__truediv__(other) + + def __getattr__(self, name): + return getattr(self.decimal, name) diff --git a/trytond/tests/test_protocols_datatype.py b/trytond/tests/test_protocols_datatype.py index 0bd1bbec14..ea865058c0 100644 --- a/trytond/tests/test_protocols_datatype.py +++ b/trytond/tests/test_protocols_datatype.py @@ -154,8 +154,10 @@ class ProtocolsDatatypeFloatTestCase(unittest.TestCase): ''' Test format Float ''' - self.assertEqual(format(Float('1.1'), '.32f'), - '1.10000000000000000000000000000000') + # For Python < 2.6 + if hasattr(__builtins__, 'format'): + self.assertEqual(format(Float('1.1'), '.32f'), + '1.10000000000000000000000000000000') def test_Float_ge_Decimal(self): ''' @@ -360,6 +362,18 @@ class ProtocolsDatatypeFloatTestCase(unittest.TestCase): ''' self.assertEqual(2.5 - Float('1.2'), 1.3) + def test_Decimal_only_Method(self): + ''' + Test Decimal only method + ''' + self.assertEqual(Float('1.5').quantize(Decimal('1')), Decimal('2')) + + def test_float_only_Method(self): + ''' + Test float only method + ''' + self.assertEqual(Float('1.5').hex(), '0x1.8000000000000p+0') + def suite(): return unittest.TestLoader().loadTestsFromTestCase(ProtocolsDatatypeFloatTestCase) From 8e16613cc49b4f19d9e3ea6af04a5d41c603004a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Tue, 8 Jun 2010 07:55:01 +0200 Subject: [PATCH 011/140] Fix Float unittest for Python < 2.6 that doesn't have hex method (transplanted from 59156e25d250b54d4d6e18f97ec708ddf2eb48c8) --- trytond/tests/test_protocols_datatype.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/trytond/tests/test_protocols_datatype.py b/trytond/tests/test_protocols_datatype.py index ea865058c0..ff5552155b 100644 --- a/trytond/tests/test_protocols_datatype.py +++ b/trytond/tests/test_protocols_datatype.py @@ -372,7 +372,9 @@ class ProtocolsDatatypeFloatTestCase(unittest.TestCase): ''' Test float only method ''' - self.assertEqual(Float('1.5').hex(), '0x1.8000000000000p+0') + # For Python < 2.6 + if hasattr(float, 'hex'): + self.assertEqual(Float('1.5').hex(), '0x1.8000000000000p+0') def suite(): return unittest.TestLoader().loadTestsFromTestCase(ProtocolsDatatypeFloatTestCase) From f341f6f68dd939bd888f4eb08e2c41497ddb72e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 9 Jun 2010 19:52:47 +0200 Subject: [PATCH 012/140] Handle date and datetime in XML-RPC (transplanted from 9a9c2ca4f50794fd13f08b09eb5093aef1a041d7) --- trytond/backend/fields.py | 8 +++++--- trytond/protocols/xmlrpc.py | 10 ++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/trytond/backend/fields.py b/trytond/backend/fields.py index 6fa95c10cf..f250fabb49 100644 --- a/trytond/backend/fields.py +++ b/trytond/backend/fields.py @@ -98,9 +98,11 @@ class Date(Field): return datetime.date(year, month, day) assert(isinstance(value, datetime.date)) - # datetime must be tested separately because datetime is a - # subclass of date: - assert(not isinstance(value, datetime.datetime)) + # Allow datetime with min time for XML-RPC + if value.time() != datetime.time(): + # datetime must be tested separately because datetime is a + # subclass of date: + assert(not isinstance(value, datetime.datetime)) return value diff --git a/trytond/protocols/xmlrpc.py b/trytond/protocols/xmlrpc.py index 41bfae69a4..1e370f7c15 100644 --- a/trytond/protocols/xmlrpc.py +++ b/trytond/protocols/xmlrpc.py @@ -16,6 +16,7 @@ import os import gzip import StringIO import base64 +import datetime # convert decimal to float before marshalling: from decimal import Decimal @@ -23,11 +24,20 @@ xmlrpclib.Marshaller.dispatch[Decimal] = \ lambda self, value, write: self.dump_double(float(value), write) xmlrpclib.Marshaller.dispatch[type(None)] = \ lambda self, value, write: self.dump_bool(bool(value), write) +xmlrpclib.Marshaller.dispatch[datetime.date] = \ + lambda self, value, write: self.dump_datetime( + datetime.datetime.combine(value, datetime.time()), write) def _end_double(self, data): self.append(Float(data)) self._value = 0 xmlrpclib.Unmarshaller.dispatch["double"] = _end_double +def _end_dateTime(self, data): + value = xmlrpclib.DateTime() + value.decode(data) + value = xmlrpclib._datetime_type(data) + self.append(value) +xmlrpclib.Unmarshaller.dispatch["dateTime.iso8601"] = _end_dateTime class GenericXMLRPCRequestHandler: From af65cfaa8cb4cdec89cb469623c894c8390b05cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 9 Jun 2010 19:58:31 +0200 Subject: [PATCH 013/140] Make _timestamp numeric instead of datetime to work with XML-RPC and JSON-RPC (transplanted from 7f5860ab74cbed21e4ef264d2c799be4c07492dd) --- CHANGELOG | 2 ++ trytond/model/modelsql.py | 37 +++++++++++++++++++++++-------------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 29567da9ab..4e818e8b6b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,5 @@ +* Make _timestamp numeric instead of datetime to work with XML-RPC and JSON-RPC + Version 1.6.0 - 2010-05-08 * Bug fixes (see mercurial logs for details) * Add symbol parameter to formatLang diff --git a/trytond/model/modelsql.py b/trytond/model/modelsql.py index 23f7afd72c..b76451a933 100644 --- a/trytond/model/modelsql.py +++ b/trytond/model/modelsql.py @@ -481,7 +481,10 @@ class ModelSQL(ModelStorage): if self._history and context.get('_datetime') and not table_query: in_max = 1 table_query = '"' + self._table + '__history" AS ' - history_clause = ' AND (COALESCE(write_date, create_date) <= %s)' + history_clause = (' AND (CAST(EXTRACT(EPOCH FROM ' + 'COALESCE(write_date, create_date)) AS ' + \ + FIELDS['numeric'].sql_type(self.create_date)[1] + \ + ') <= %s)') history_order = ' ORDER BY COALESCE(write_date, create_date) DESC' history_limit = cursor.limit_clause('', 1) history_args = [context['_datetime']] @@ -490,8 +493,10 @@ class ModelSQL(ModelStorage): if x != '_timestamp'] if '_timestamp' in fields_pre: if not self.table_query(context): - fields_pre2 += ['(COALESCE(write_date, create_date)) ' \ - 'AS _timestamp'] + fields_pre2 += ['CAST(EXTRACT(EPOCH FROM ' + '(COALESCE(write_date, create_date))) AS ' + \ + FIELDS['numeric'].sql_type(self.create_date)[1] + \ + ') AS _timestamp'] for i in range(0, len(ids), in_max): sub_ids = ids[i:i + in_max] @@ -745,10 +750,11 @@ class ModelSQL(ModelStorage): if context.get('_timestamp', False): for i in range(0, len(ids), cursor.IN_MAX): sub_ids = ids[i:i + cursor.IN_MAX] - clause = '(id = %s AND ' \ - '(CASE WHEN write_date IS NOT NULL ' \ - 'THEN write_date ELSE create_date END) ' \ - ' > %s)' + clause = ('(id = %s AND ' + 'CAST(EXTRACT(EPOCH FROM ' + 'COALESCE(write_date, create_date)) AS ' + \ + FIELDS['numeric'].sql_type(self.create_date)[1] + \ + ') > %s)') args = [] for i in sub_ids: if context['_timestamp'].get(self._name + ',' + str(i)): @@ -987,10 +993,11 @@ class ModelSQL(ModelStorage): if context.get('_timestamp', False): for i in range(0, len(ids), cursor.IN_MAX): sub_ids = ids[i:i + cursor.IN_MAX] - clause = '(id = %s AND ' \ - '(CASE WHEN write_date IS NOT NULL ' \ - 'THEN write_date ELSE create_date END) ' \ - ' > %s)' + clause = ('(id = %s AND ' + 'CAST(EXTRACT(EPOCH FROM ' + 'COALESCE(write_date, create_date)) AS ' + \ + FIELDS['numeric'].sql_type(self.create_date)[1] + \ + ') > %s)') args = [] for i in sub_ids: if context['_timestamp'].get(self._name + ',' + str(i)): @@ -1212,9 +1219,11 @@ class ModelSQL(ModelStorage): and (not getattr(x[1], 'translate', False) \ and x[1]._type not in ('text', 'binary'))] if not self.table_query(context): - select_fields += ['(COALESCE("' + self._table + '".write_date,'\ - '"' + self._table + '".create_date)) ' \ - 'AS _timestamp'] + select_fields += ['CAST(EXTRACT(EPOCH FROM ' + '(COALESCE("' + self._table + '".write_date, ' + '"' + self._table + '".create_date))) AS ' + \ + FIELDS['numeric'].sql_type(self.create_date)[1] + \ + ') AS _timestamp'] query_str = cursor.limit_clause( 'SELECT ' + ','.join(select_fields) + ' FROM ' + \ ' '.join(tables) + (qu1 and ' WHERE ' + qu1 or '') + \ From 2a2fa25aa1e185bfdb8a99a63d456cb12ef62ea4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 9 Jun 2010 20:30:01 +0200 Subject: [PATCH 014/140] Fix test for datetime in Date fields (transplanted from 43bf75cf91c018d2a5b20646c3569d16a8cb2965) --- trytond/backend/fields.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/trytond/backend/fields.py b/trytond/backend/fields.py index f250fabb49..ec1d538d87 100644 --- a/trytond/backend/fields.py +++ b/trytond/backend/fields.py @@ -99,10 +99,10 @@ class Date(Field): assert(isinstance(value, datetime.date)) # Allow datetime with min time for XML-RPC - if value.time() != datetime.time(): - # datetime must be tested separately because datetime is a - # subclass of date: - assert(not isinstance(value, datetime.datetime)) + # datetime must be tested separately because datetime is a + # subclass of date + assert(not isinstance(value, datetime.datetime) + or value.time() == datetime.time()) return value From 30d90b99c3c2fe04996fef65ac141088450f495a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 12 Jun 2010 20:16:25 +0200 Subject: [PATCH 015/140] Fix history clause error introduced by changeset 7f5860ab74cb (transplanted from 012afdea6fe76da72b6ad683acea3f67d00fcf6d) --- trytond/model/modelsql.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/trytond/model/modelsql.py b/trytond/model/modelsql.py index b76451a933..ab3cde262c 100644 --- a/trytond/model/modelsql.py +++ b/trytond/model/modelsql.py @@ -481,10 +481,7 @@ class ModelSQL(ModelStorage): if self._history and context.get('_datetime') and not table_query: in_max = 1 table_query = '"' + self._table + '__history" AS ' - history_clause = (' AND (CAST(EXTRACT(EPOCH FROM ' - 'COALESCE(write_date, create_date)) AS ' + \ - FIELDS['numeric'].sql_type(self.create_date)[1] + \ - ') <= %s)') + history_clause = ' AND (COALESCE(write_date, create_date) <= %s)' history_order = ' ORDER BY COALESCE(write_date, create_date) DESC' history_limit = cursor.limit_clause('', 1) history_args = [context['_datetime']] From 52e72d4343326a6d82a82d28aa4eecd6c14cb61f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 17 Jun 2010 20:05:59 +0200 Subject: [PATCH 016/140] Always check on delete restrict for issue1588 (transplanted from aab1c133077c144b83bbe6871fac3b2bae90eb8c) --- trytond/model/modelsql.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/trytond/model/modelsql.py b/trytond/model/modelsql.py index ab3cde262c..c36758afe0 100644 --- a/trytond/model/modelsql.py +++ b/trytond/model/modelsql.py @@ -1100,6 +1100,18 @@ class ModelSQL(ModelStorage): if model_ids: model.delete(cursor, user, model_ids, context=delete_ctx) + for model, field_name in foreign_keys_tocheck: + if model.search(cursor, 0, [ + (field_name, 'in', sub_ids), + ], order=[], context=context): + error_args = [] + error_args.append(self._get_error_args(cursor, user, 'id', + context=context)[1]) + error_args.extend(list(model._get_error_args(cursor, + user, field_name, context=context))) + self.raise_user_error(cursor, 'foreign_model_exist', + error_args=tuple(error_args), context=context) + super(ModelSQL, self).delete(cursor, user, sub_ids, context=context) try: @@ -1109,19 +1121,6 @@ class ModelSQL(ModelStorage): database = Database(cursor.database_name).connect() cursor2 = database.cursor() try: - for model, field_name in foreign_keys_tocheck: - if model.search(cursor2, 0, [ - (field_name, 'in', sub_ids), - ], order=[], context=context): - error_args = [] - error_args.append(self._get_error_args(cursor2, - user, 'id', context=context)[1]) - error_args.extend(list(model._get_error_args( - cursor2, user, field_name, context=context))) - self.raise_user_error(cursor2, - 'foreign_model_exist', - error_args=tuple(error_args), - context=context) for name, _, error in self._sql_constraints: if name in exception[0]: self.raise_user_error(cursor2, error, From a09a67f47c0211bb0e20d864317ed127c9a2346b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 28 Jun 2010 21:25:33 +0200 Subject: [PATCH 017/140] Fix _clean_defaults for one2many inherited (transplanted from 1c802de12b7ad6772d9bde56cd7b079efafdf3e9) --- trytond/model/modelstorage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trytond/model/modelstorage.py b/trytond/model/modelstorage.py index 2fddf09cff..9411ac4e64 100644 --- a/trytond/model/modelstorage.py +++ b/trytond/model/modelstorage.py @@ -1113,7 +1113,7 @@ class ModelStorage(Model): else: vals[field] = defaults[field] elif fld_def._type in ('one2many',): - obj = self.pool.get(self._columns[field].model_name) + obj = self.pool.get(fld_def.model_name) vals[field] = [] for defaults2 in defaults[field]: vals2 = obj._clean_defaults(defaults2) From d0b6113a3e5b0470b26fd634264d262cc840504d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 2 Jul 2010 18:41:26 +0200 Subject: [PATCH 018/140] Update manifest with files added from style for issue1507 (transplanted from 3888542922ca3ebc0702facbd73e9d983bea57de) --- trytond/report/report.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/trytond/report/report.py b/trytond/report/report.py index 66ebd67bb3..773d76a5d2 100644 --- a/trytond/report/report.py +++ b/trytond/report/report.py @@ -21,6 +21,10 @@ import warnings warnings.simplefilter("ignore") import relatorio.reporting warnings.resetwarnings() +try: + from relatorio.templates.opendocument import Manifest, MANIFEST +except ImportError: + Manifest = None import tempfile from genshi.filters import Translator @@ -161,11 +165,15 @@ class Report(object): style_info = None style_xml = None + manifest = None for f in content_z.infolist(): if f.filename == 'styles.xml' and report.style_content: style_info = f style_xml = content_z.read(f.filename) continue + elif Manifest and f.filename == MANIFEST: + manifest = Manifest(content_z.read(f.filename)) + continue outzip.writestr(f, content_z.read(f.filename)) if report.style_content: @@ -183,6 +191,7 @@ class Report(object): if file.startswith('Pictures'): picture = style2_z.read(file) pictures.append((file, picture)) + manifest.add_file_entry(file) style2_z.close() style2_io.close() dom_style2 = xml.dom.minidom.parseString(style2_xml) @@ -203,6 +212,9 @@ class Report(object): for file, picture in pictures: outzip.writestr(file, picture) + if manifest: + outzip.writestr(MANIFEST, str(manifest)) + content_z.close() content_io.close() outzip.close() From 601f86fa940fdbe97d573ef4f3753093512fb641 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 4 Jul 2010 22:56:23 +0200 Subject: [PATCH 019/140] Fix DateTime microsecond replacement replace method return a new datetime (transplanted from 9fa480f96eba384f411b9369347d5913d92bbd15) --- trytond/backend/fields.py | 3 +-- trytond/tests/test_fields.py | 8 ++++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/trytond/backend/fields.py b/trytond/backend/fields.py index ec1d538d87..0919c26ef1 100644 --- a/trytond/backend/fields.py +++ b/trytond/backend/fields.py @@ -118,8 +118,7 @@ class DateTime(Field): hours, minutes, seconds = map(int, timepart.split(":")) return datetime.datetime(year, month, day, hours, minutes, seconds) assert(isinstance(value, datetime.datetime)) - value.replace(microsecond=0) - return value + return value.replace(microsecond=0) class Timestamp(Field): diff --git a/trytond/tests/test_fields.py b/trytond/tests/test_fields.py index 28c06f6f47..f263dd570b 100644 --- a/trytond/tests/test_fields.py +++ b/trytond/tests/test_fields.py @@ -2114,6 +2114,14 @@ class FieldsTestCase(unittest.TestCase): }, CONTEXT) self.assert_(datetime8_id) + datetime9_id = self.datetime.create(cursor, USER, { + 'datetime': today.replace(microsecond=1), + }, CONTEXT) + self.assert_(datetime9_id) + datetime9 = self.datetime.read(cursor, USER, datetime9_id, + ['datetime'], CONTEXT) + self.assert_(datetime9['datetime'] == today) + cursor.rollback() cursor.close() From 1b662769bf952698d74f6d2bbc567984e1ff1d58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 8 Jul 2010 20:01:11 +0200 Subject: [PATCH 020/140] Fix extract function in SQLite to work with date (transplanted from faf73da610031ab5d7b49e89c4aca221846b10d9) --- trytond/backend/sqlite/database.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/trytond/backend/sqlite/database.py b/trytond/backend/sqlite/database.py index 6f4bb89637..826a14a5fe 100644 --- a/trytond/backend/sqlite/database.py +++ b/trytond/backend/sqlite/database.py @@ -28,14 +28,18 @@ def extract(lookup_type, date): if date is None: return None try: - datepart, timepart = date.split(" ") - year, month, day = map(int, datepart.split("-")) - timepart_full = timepart.split(".") - hours, minutes, seconds = map(int, timepart_full[0].split(":")) - if len(timepart_full) == 2: - microseconds = int(timepart_full[1]) - date = datetime.datetime(year, month, day, hours, minutes, seconds, - microseconds) + if len(date) == 10: + year, month, day = map(int, date.split('-')) + date = datetime.date(year, month, day) + else: + datepart, timepart = date.split(" ") + year, month, day = map(int, datepart.split("-")) + timepart_full = timepart.split(".") + hours, minutes, seconds = map(int, timepart_full[0].split(":")) + if len(timepart_full) == 2: + microseconds = int(timepart_full[1]) + date = datetime.datetime(year, month, day, hours, minutes, seconds, + microseconds) except: return None if lookup_type.lower() == 'century': @@ -58,7 +62,7 @@ def extract(lookup_type, date): return date.month / 4 + 1 elif lookup_type.lower() == 'week': return date.isocalendar()[1] - return getattr(date, lookup_type) + return getattr(date, lookup_type.lower()) def date_trunc(_type, date): if _type == 'second': From 7673edf0df41a68ca12174f55e103420fa1a4323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 11 Jul 2010 15:29:29 +0200 Subject: [PATCH 021/140] Fix typo in test query for PostgreSQL and SQLite thanks to Ralf Peschke (transplanted from d1bddcf64ea7cd63d7a1611a5866f1b491734076) --- trytond/backend/postgresql/database.py | 4 ++-- trytond/backend/sqlite/database.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/trytond/backend/postgresql/database.py b/trytond/backend/postgresql/database.py index c231270671..d737148de0 100644 --- a/trytond/backend/postgresql/database.py +++ b/trytond/backend/postgresql/database.py @@ -364,8 +364,8 @@ class Cursor(CursorInterface): "'wkf_workitem', " "'wkf_witm_trans', " "'ir_module_module', " - "'ir_module_module_dependency, '" - "'ir_translation, '" + "'ir_module_module_dependency', " + "'ir_translation', " "'ir_lang'" ")") return len(self.cursor.fetchall()) != 0 diff --git a/trytond/backend/sqlite/database.py b/trytond/backend/sqlite/database.py index 826a14a5fe..40b21d472b 100644 --- a/trytond/backend/sqlite/database.py +++ b/trytond/backend/sqlite/database.py @@ -330,8 +330,8 @@ class Cursor(CursorInterface): "'wkf_workitem', " "'wkf_witm_trans', " "'ir_module_module', " - "'ir_module_module_dependency, '" - "'ir_translation, '" + "'ir_module_module_dependency', " + "'ir_translation', " "'ir_lang'" ")") except: From 5e3552ae3e2a7b689201f5b600ed5d880c8917fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 15 Jul 2010 18:58:26 +0200 Subject: [PATCH 022/140] Fix wrong value passed to super call (transplanted from 1636812276c3fc4e38ad1831aff988feeb704958) --- trytond/model/modelsingleton.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/trytond/model/modelsingleton.py b/trytond/model/modelsingleton.py index 4e08f11a46..8c53ec5db2 100644 --- a/trytond/model/modelsingleton.py +++ b/trytond/model/modelsingleton.py @@ -84,7 +84,8 @@ class ModelSingleton(ModelStorage): def search(self, cursor, user, domain, offset=0, limit=None, order=None, context=None, count=False): res = super(ModelSingleton, self).search(cursor, user, domain, - offset=0, limit=None, order=None, context=None, count=False) + offset=offset, limit=limit, order=order, context=context, + count=count) if not res: if count: return 1 From c6aa9c912c3ddf15e6f85e59ab364bec649b0c22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 15 Jul 2010 19:00:21 +0200 Subject: [PATCH 023/140] Use explicit AS column name in queries that use dictfetch* SQLite return wrong column name when using query as table (transplanted from 5c38493067b4de52ece25abaf4353bc48f312299) --- trytond/model/modelsql.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/trytond/model/modelsql.py b/trytond/model/modelsql.py index c36758afe0..336bd16b43 100644 --- a/trytond/model/modelsql.py +++ b/trytond/model/modelsql.py @@ -486,21 +486,23 @@ class ModelSQL(ModelStorage): history_limit = cursor.limit_clause('', 1) history_args = [context['_datetime']] if len(fields_pre) : - fields_pre2 = ['"' + x + '"' for x in fields_pre \ - if x != '_timestamp'] + fields_pre2 = ['"%s"."%s" AS "%s"' % (self._table, x, x) + for x in fields_pre if x != '_timestamp'] if '_timestamp' in fields_pre: if not self.table_query(context): fields_pre2 += ['CAST(EXTRACT(EPOCH FROM ' - '(COALESCE(write_date, create_date))) AS ' + \ - FIELDS['numeric'].sql_type(self.create_date)[1] + \ - ') AS _timestamp'] + '(COALESCE("%s".write_date, "%s".create_date))) AS %s' + ') AS _timestamp' % + (self._table, self._table, + FIELDS['numeric'].sql_type(self.create_date)[1])] for i in range(0, len(ids), in_max): sub_ids = ids[i:i + in_max] red_sql, red_ids = reduce_ids('id', sub_ids) if domain1: cursor.execute('SELECT ' + \ - ','.join(fields_pre2 + ['id']) + \ + ','.join(fields_pre2 + + ['"%s".id AS id' % self._table]) + \ ' FROM ' + table_query + '\"' + self._table +'\" ' \ 'WHERE ' + red_sql + \ history_clause + \ @@ -509,7 +511,8 @@ class ModelSQL(ModelStorage): table_args + red_ids + history_args + domain2) else: cursor.execute('SELECT ' + \ - ','.join(fields_pre2 + ['id']) + \ + ','.join(fields_pre2 + + ['"%s".id AS id' % self._table]) + \ ' FROM ' + table_query + '\"' + self._table + '\" '\ 'WHERE ' + red_sql + \ history_clause + history_order + history_limit, From f39279b2b2117287ac8d4f2ede6642ce312d2593 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 18 Jul 2010 22:33:49 +0200 Subject: [PATCH 024/140] Remove properties tag from documentation This tag was never used (transplanted from b6192e7460fe536991237b1de96fd01b13ea4335) --- doc/topics/views/index.rst | 8 -------- 1 file changed, 8 deletions(-) diff --git a/doc/topics/views/index.rst b/doc/topics/views/index.rst index 06fe31b0f0..6826953b21 100644 --- a/doc/topics/views/index.rst +++ b/doc/topics/views/index.rst @@ -253,11 +253,6 @@ child1, child2 Contains the two childs of a hpaned or vpaned. -properties -^^^^^^^^^^ - -Display a table of 4 columns with all the properties of the object. - .. _example_form_view: Example @@ -293,9 +288,6 @@ Example